RANGER-995 : CSRF implementation in Ranger

Signed-off-by: Gautam Borad <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/e1150005
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/e1150005
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/e1150005

Branch: refs/heads/master
Commit: e11500050d32845441c96adee45d4289624dbf85
Parents: 7d45206
Author: Ankita Sinha <[email protected]>
Authored: Wed May 25 12:19:42 2016 +0530
Committer: Gautam Borad <[email protected]>
Committed: Mon May 30 17:58:10 2016 +0530

----------------------------------------------------------------------
 .../org/apache/ranger/rest/ServiceREST.java     |  22 ++
 .../web/filter/RangerCSRFPreventionFilter.java  | 229 +++++++++++++++++++
 .../resources/conf.dist/ranger-admin-site.xml   |  18 ++
 .../conf.dist/security-applicationContext.xml   |   4 +
 security-admin/src/main/webapp/scripts/Main.js  |   3 +-
 .../src/main/webapp/scripts/modules/RestCsrf.js |  98 ++++++++
 .../filter/TestRangerCSRFPreventionFilter.java  | 152 ++++++++++++
 7 files changed, 525 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e1150005/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java 
b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
index 052254d..886e78f 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
@@ -56,6 +56,7 @@ import org.apache.ranger.biz.XUserMgr;
 import org.apache.ranger.common.ContextUtil;
 import org.apache.ranger.common.GUIDUtil;
 import org.apache.ranger.common.MessageEnums;
+import org.apache.ranger.common.PropertiesUtil;
 import org.apache.ranger.common.RESTErrorUtil;
 import org.apache.ranger.common.RangerSearchUtil;
 import org.apache.ranger.common.RangerValidatorFactory;
@@ -114,6 +115,11 @@ public class ServiceREST {
        private static final String Allowed_User_List_For_Download = 
"policy.download.auth.users";
        private static final String Allowed_User_List_For_Grant_Revoke = 
"policy.grantrevoke.auth.users";
 
+       public static final String isCSRF_ENABLED = "ranger.rest-csrf.enabled";
+       public static final String BROWSER_USER_AGENT_PARAM = 
"ranger.rest-csrf.browser-useragents-regex";
+       public static final String CUSTOM_METHODS_TO_IGNORE_PARAM = 
"ranger.rest-csrf.methods-to-ignore";
+       public static final String CUSTOM_HEADER_PARAM = 
"ranger.rest-csrf.custom-header";
+       
        @Autowired
        RESTErrorUtil restErrorUtil;
 
@@ -2248,7 +2254,23 @@ public class ServiceREST {
        public String checkSSO() {
                return String.valueOf(bizUtil.isSSOEnabled());
        }
+       
+       @GET
+       @Path("/csrfconf")
+       @Produces({ "application/json"})
+       public HashMap<String, Object> getCSRFProperties() {
+               return getCSRFPropertiesMap();
+       }
 
+       private HashMap<String, Object> getCSRFPropertiesMap() {
+               HashMap<String, Object> map = new HashMap<String, Object>();  
+               map.put(isCSRF_ENABLED, 
PropertiesUtil.getBooleanProperty(isCSRF_ENABLED, false));
+               map.put(CUSTOM_HEADER_PARAM, 
PropertiesUtil.getProperty(CUSTOM_HEADER_PARAM));
+               map.put(BROWSER_USER_AGENT_PARAM, 
PropertiesUtil.getProperty(BROWSER_USER_AGENT_PARAM));
+               map.put(CUSTOM_METHODS_TO_IGNORE_PARAM, 
PropertiesUtil.getProperty(CUSTOM_METHODS_TO_IGNORE_PARAM));
+               return map;
+       }
+       
        boolean isAdminUserWithNoFilterParams(SearchFilter filter) {
                return (filter == null || MapUtils.isEmpty(filter.getParams())) 
&&
                           (bizUtil.isAdmin() || bizUtil.isKeyAdmin());

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e1150005/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerCSRFPreventionFilter.java
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerCSRFPreventionFilter.java
 
b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerCSRFPreventionFilter.java
new file mode 100644
index 0000000..42b4ad4
--- /dev/null
+++ 
b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerCSRFPreventionFilter.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.security.web.filter;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.apache.ranger.common.PropertiesUtil;
+
+public class RangerCSRFPreventionFilter implements Filter {
+       
+       private static final Logger LOG = 
Logger.getLogger(RangerCSRFPreventionFilter.class);
+               
+       public static final boolean isCSRF_ENABLED = 
PropertiesUtil.getBooleanProperty("ranger.rest-csrf.enabled",true);
+       public static final String BROWSER_USER_AGENT_PARAM = 
"ranger.rest-csrf.browser-useragents-regex";
+       static final String  BROWSER_USER_AGENTS_DEFAULT = 
"^Mozilla.*,^Opera.*";
+       public static final String CUSTOM_METHODS_TO_IGNORE_PARAM = 
"ranger.rest-csrf.methods-to-ignore";
+       static final String  METHODS_TO_IGNORE_DEFAULT = 
"GET,OPTIONS,HEAD,TRACE";
+       public static final String CUSTOM_HEADER_PARAM = 
"ranger.rest-csrf.custom-header";
+       public static final String HEADER_DEFAULT = "X-XSRF-HEADER";
+       public static final String HEADER_USER_AGENT = "User-Agent";
+
+       private String  headerName = HEADER_DEFAULT;
+       private Set<String> methodsToIgnore = null;
+       private Set<Pattern> browserUserAgents;
+       
+       public RangerCSRFPreventionFilter() {
+               try {
+                       if (isCSRF_ENABLED){
+                               init(null);
+                       }
+               } catch (Exception e) {
+                       LOG.error("Error while initializing Filter : 
"+e.getMessage());
+               }
+       }
+       
+       public void init(FilterConfig filterConfig) throws ServletException {
+               String customHeader = 
PropertiesUtil.getProperty(CUSTOM_HEADER_PARAM);
+           if (customHeader != null) {
+             headerName = customHeader;
+           }
+           
+           String customMethodsToIgnore = 
PropertiesUtil.getProperty(CUSTOM_METHODS_TO_IGNORE_PARAM);
+        if (customMethodsToIgnore != null) {
+          parseMethodsToIgnore(customMethodsToIgnore);
+        } else {
+          parseMethodsToIgnore(METHODS_TO_IGNORE_DEFAULT);
+        }
+        String agents = PropertiesUtil.getProperty(BROWSER_USER_AGENT_PARAM);
+        if (agents == null) {
+          agents = BROWSER_USER_AGENTS_DEFAULT;
+        }
+        parseBrowserUserAgents(agents);
+        LOG.info("Adding cross-site request forgery (CSRF) protection");
+       }
+       
+       void parseMethodsToIgnore(String mti) {
+        String[] methods = mti.split(",");
+        methodsToIgnore = new HashSet<String>();
+        for (int i = 0; i < methods.length; i++) {
+          methodsToIgnore.add(methods[i]);
+        }
+       }
+       
+       void parseBrowserUserAgents(String userAgents) {
+               String[] agentsArray = userAgents.split(",");
+               browserUserAgents = new HashSet<Pattern>();
+               for (String patternString : agentsArray) {
+                       browserUserAgents.add(Pattern.compile(patternString));
+               }
+       }
+       
+       protected boolean isBrowser(String userAgent) {
+               if (userAgent == null) {
+                       return false;
+               }
+               if (browserUserAgents != null){
+                       for (Pattern pattern : browserUserAgents) {
+                               Matcher matcher = pattern.matcher(userAgent);
+                               if (matcher.matches()) {
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+         
+       public interface HttpInteraction {
+               /**
+                * Returns the value of a header.
+                *
+                * @param header
+                *            name of header
+                * @return value of header
+                */
+               String getHeader(String header);
+
+               /**
+                * Returns the method.
+                *
+                * @return method
+                */
+               String getMethod();
+
+               /**
+                * Called by the filter after it decides that the request may 
proceed.
+                *
+                * @throws IOException
+                *             if there is an I/O error
+                * @throws ServletException
+                *             if the implementation relies on the servlet API 
and a
+                *             servlet API call has failed
+                */
+               void proceed() throws IOException, ServletException;
+
+               /**
+                * Called by the filter after it decides that the request is a 
potential
+                * CSRF attack and therefore must be rejected.
+                *
+                * @param code
+                *            status code to send
+                * @param message
+                *            response message
+                * @throws IOException
+                *             if there is an I/O error
+                */
+               void sendError(int code, String message) throws IOException;
+       }       
+         
+       public void handleHttpInteraction(HttpInteraction httpInteraction)
+                       throws IOException, ServletException {
+               if (!isBrowser(httpInteraction.getHeader(HEADER_USER_AGENT))
+                               || 
methodsToIgnore.contains(httpInteraction.getMethod())
+                               || httpInteraction.getHeader(headerName) != 
null) {
+                       httpInteraction.proceed();
+               }else {
+                       
httpInteraction.sendError(HttpServletResponse.SC_BAD_REQUEST,"Missing Required 
Header for CSRF Vulnerability Protection");
+               }
+       }
+       
+       public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException {
+               if (isCSRF_ENABLED){
+                       final HttpServletRequest httpRequest = 
(HttpServletRequest)request;
+                   final HttpServletResponse httpResponse = 
(HttpServletResponse)response;
+                   handleHttpInteraction(new 
ServletFilterHttpInteraction(httpRequest, httpResponse, chain));
+               }else{
+                       chain.doFilter(request, response);
+               }
+       }
+
+       public void destroy() {
+       }
+       
+       private static final class ServletFilterHttpInteraction implements
+                       HttpInteraction {
+
+               private final FilterChain chain;
+               private final HttpServletRequest httpRequest;
+               private final HttpServletResponse httpResponse;
+
+               /**
+                * Creates a new ServletFilterHttpInteraction.
+                *
+                * @param httpRequest
+                *            request to process
+                * @param httpResponse
+                *            response to process
+                * @param chain
+                *            filter chain to forward to if HTTP interaction is 
allowed
+                */
+               public ServletFilterHttpInteraction(HttpServletRequest 
httpRequest,
+                               HttpServletResponse httpResponse, FilterChain 
chain) {
+                       this.httpRequest = httpRequest;
+                       this.httpResponse = httpResponse;
+                       this.chain = chain;
+               }
+
+               @Override
+               public String getHeader(String header) {
+                       return httpRequest.getHeader(header);
+               }
+
+               @Override
+               public String getMethod() {
+                       return httpRequest.getMethod();
+               }
+
+               @Override
+               public void proceed() throws IOException, ServletException {
+                       chain.doFilter(httpRequest, httpResponse);
+               }
+
+               @Override
+               public void sendError(int code, String message) throws 
IOException {
+                       httpResponse.sendError(code, message);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e1150005/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
----------------------------------------------------------------------
diff --git a/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml 
b/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
index c1a91ae..60a2c96 100644
--- a/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
+++ b/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
@@ -288,4 +288,22 @@
         <name>ranger.kms.service.user.hive</name>
         <value>hive</value>
     </property>
+       <!--  CSRF Properties Starts-->
+       <property>
+               <name>ranger.rest-csrf.enabled</name>
+               <value>true</value>
+       </property>
+       <property>
+               <name>ranger.rest-csrf.custom-header</name>
+               <value>X-XSRF-HEADER</value>
+       </property>
+       <property>
+               <name>ranger.rest-csrf.methods-to-ignore</name>
+               <value>GET,OPTIONS,HEAD,TRACE</value>
+       </property>
+       <property>
+               <name>ranger.rest-csrf.browser-useragents-regex</name>
+               <value>^Mozilla.*,^Opera.*</value>
+       </property>
+       <!--  CSRF Properties ENDs-->
 </configuration>

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e1150005/security-admin/src/main/resources/conf.dist/security-applicationContext.xml
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/resources/conf.dist/security-applicationContext.xml 
b/security-admin/src/main/resources/conf.dist/security-applicationContext.xml
index 66ef8af..13ddb26 100644
--- 
a/security-admin/src/main/resources/conf.dist/security-applicationContext.xml
+++ 
b/security-admin/src/main/resources/conf.dist/security-applicationContext.xml
@@ -50,6 +50,7 @@ 
http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd";>
                <intercept-url pattern="/**" access="isAuthenticated()"/>       
                <custom-filter ref="ssoAuthenticationFilter" 
after="BASIC_AUTH_FILTER" /> 
                <security:custom-filter ref="krbAuthenticationFilter" 
after="SERVLET_API_SUPPORT_FILTER" />
+               <security:custom-filter ref="CSRFPreventionFilter" 
after="REMEMBER_ME_FILTER" />
                <security:custom-filter position="FORM_LOGIN_FILTER" 
ref="customUsernamePasswordAuthenticationFilter"/>
                <security:custom-filter position="LAST" 
ref="userContextFormationFilter"/>
 
@@ -93,6 +94,9 @@ 
http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd";>
        <beans:bean id="krbAuthenticationFilter" 
class="org.apache.ranger.security.web.filter.RangerKRBAuthenticationFilter">
     </beans:bean>
 
+       <beans:bean id="CSRFPreventionFilter" 
class="org.apache.ranger.security.web.filter.RangerCSRFPreventionFilter">
+    </beans:bean>
+
     <beans:bean id="ssoAuthenticationFilter" 
class="org.apache.ranger.security.web.filter.RangerSSOAuthenticationFilter">
     </beans:bean>
        

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e1150005/security-admin/src/main/webapp/scripts/Main.js
----------------------------------------------------------------------
diff --git a/security-admin/src/main/webapp/scripts/Main.js 
b/security-admin/src/main/webapp/scripts/Main.js
index 460c91a..d518afb 100644
--- a/security-admin/src/main/webapp/scripts/Main.js
+++ b/security-admin/src/main/webapp/scripts/Main.js
@@ -24,10 +24,11 @@
        'routers/Router',
        'controllers/Controller',
        'modules/XAOverrides',
+       'modules/RestCsrf',
        'utils/XAUtils',
        'hbs!tmpl/common/loading_tmpl'
 ],
-function ( Backbone, App, RegionManager, AppRouter, AppController, 
XAOverrides, XAUtils, loadingHTML ) {
+function ( Backbone, App, RegionManager, AppRouter, AppController, 
XAOverrides,RestCSRF, XAUtils, loadingHTML ) {
     'use strict';
 
     var controller = new AppController();

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e1150005/security-admin/src/main/webapp/scripts/modules/RestCsrf.js
----------------------------------------------------------------------
diff --git a/security-admin/src/main/webapp/scripts/modules/RestCsrf.js 
b/security-admin/src/main/webapp/scripts/modules/RestCsrf.js
new file mode 100644
index 0000000..2eff355
--- /dev/null
+++ b/security-admin/src/main/webapp/scripts/modules/RestCsrf.js
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//"use strict";
+
+// Initializes client-side handling of cross-site request forgery (CSRF)
+// protection by figuring out the custom HTTP headers that need to be sent in
+// requests and which HTTP methods are ignored because they do not require CSRF
+// protection.
+(function() {
+       "use strict";
+       require('jquery');
+       var restCsrfCustomHeader = null;
+       var restCsrfMethodsToIgnore = null;
+
+       if(!window.location.origin){
+               window.location.origin = window.location.protocol + "//" + 
window.location.hostname + (window.location.port ? ':' + window.location.port: 
'');
+       }
+       var baseUrl = window.location.origin +
+                                       
window.location.pathname.substring(window.location.pathname.indexOf('/', 2) + 
1, 0);
+       if(baseUrl.slice(-1) == "/") {
+         baseUrl = baseUrl.slice(0,-1);
+       }
+       var url = baseUrl + "/service/plugins/csrfconf";
+
+  $.ajax({'url': url, 'dataType': 'json', 'async': false}).done(
+    function(data) {
+       function getTrimmedStringArrayValue(element) {
+               var str = element, array = [];
+               if (str) {
+                       var splitStr = str.split(',');
+                       for (var i = 0; i < splitStr.length; i++) {
+                               array.push(splitStr[i].trim());
+                       }
+               }
+               return array;
+      }
+
+      // Get all relevant configuration properties.
+      var $xml = $(data);
+      var csrfEnabled = false;
+      var header = null;
+      var methods = [];
+      $xml.each(function(indx,element){
+         if(element['ranger.rest-csrf.enabled']) {
+                 var str = "" + element['ranger.rest-csrf.enabled'];
+                 csrfEnabled = (str.toLowerCase() == 'true');
+         }
+         if (element['ranger.rest-csrf.custom-header']) {
+                 header = element['ranger.rest-csrf.custom-header'].trim();
+         }
+         if (element['ranger.rest-csrf.methods-to-ignore']) {
+                 methods = 
getTrimmedStringArrayValue(element['ranger.rest-csrf.methods-to-ignore']);
+         }
+      });
+
+      // If enabled, set up all subsequent AJAX calls with a pre-send callback
+      // that adds the custom headers if necessary.
+      if (csrfEnabled) {
+        restCsrfCustomHeader = header;
+        restCsrfMethodsToIgnore = {};
+        methods.map(function(method) { restCsrfMethodsToIgnore[method] = true; 
});
+        $.ajaxSetup({
+          beforeSend: addRestCsrfCustomHeader
+        });
+      }
+    });
+
+  // Adds custom headers to request if necessary.  This is done only for 
WebHDFS
+  // URLs, and only if it's not an ignored method.
+  function addRestCsrfCustomHeader(xhr, settings) {
+//    if (settings.url == null || !settings.url.startsWith('/webhdfs/')) {
+         if (settings.url == null ) {
+      return;
+    }
+    var method = settings.type;
+    if (restCsrfCustomHeader != null && !restCsrfMethodsToIgnore[method]) {
+      // The value of the header is unimportant.  Only its presence matters.
+      xhr.setRequestHeader(restCsrfCustomHeader, '""');
+    }
+  }
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e1150005/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerCSRFPreventionFilter.java
----------------------------------------------------------------------
diff --git 
a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerCSRFPreventionFilter.java
 
b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerCSRFPreventionFilter.java
new file mode 100644
index 0000000..f15def4
--- /dev/null
+++ 
b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerCSRFPreventionFilter.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ranger.security.web.filter;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+
+public class TestRangerCSRFPreventionFilter {
+       
+       private static final String EXPECTED_MESSAGE = "Missing Required Header 
for CSRF Vulnerability Protection";
+       private static final String X_CUSTOM_HEADER = "X-CUSTOM_HEADER";
+       private String userAgent = "Mozilla";
+       
+       @Test
+       public void testNoHeaderDefaultConfig_badRequest() throws 
ServletException, IOException {
+               // CSRF has not been sent
+               HttpServletRequest mockReq = 
Mockito.mock(HttpServletRequest.class);
+               
Mockito.when(mockReq.getHeader(RangerCSRFPreventionFilter.HEADER_DEFAULT)).thenReturn(null);
+               
Mockito.when(mockReq.getHeader(RangerCSRFPreventionFilter.HEADER_USER_AGENT)).thenReturn(userAgent);
            
+
+               // Objects to verify interactions based on request
+               HttpServletResponse mockRes = 
Mockito.mock(HttpServletResponse.class);
+               FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+               // Object under test
+               RangerCSRFPreventionFilter filter = new 
RangerCSRFPreventionFilter();
+               filter.doFilter(mockReq, mockRes, mockChain);
+
+               verify(mockRes, 
atLeastOnce()).sendError(HttpServletResponse.SC_BAD_REQUEST, EXPECTED_MESSAGE);
+               Mockito.verifyZeroInteractions(mockChain);
+       }
+       
+       @Test
+       public void testHeaderPresentDefaultConfig_goodRequest() throws 
ServletException, IOException {
+               // CSRF HAS been sent
+               HttpServletRequest mockReq = 
Mockito.mock(HttpServletRequest.class);
+               
Mockito.when(mockReq.getHeader(RangerCSRFPreventionFilter.HEADER_DEFAULT)).thenReturn("valueUnimportant");
+               
Mockito.when(mockReq.getHeader(RangerCSRFPreventionFilter.HEADER_USER_AGENT)).thenReturn(userAgent);
+
+               // Objects to verify interactions based on request
+               HttpServletResponse mockRes = 
Mockito.mock(HttpServletResponse.class);
+               FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+               // Object under test
+               RangerCSRFPreventionFilter filter = new 
RangerCSRFPreventionFilter();
+               filter.doFilter(mockReq, mockRes, mockChain);
+
+               Mockito.verify(mockChain).doFilter(mockReq, mockRes);
+       }
+
+       @Test
+       public void testHeaderPresentCustomHeaderConfig_goodRequest() throws 
ServletException, IOException {
+               // CSRF HAS been sent
+               HttpServletRequest mockReq = 
Mockito.mock(HttpServletRequest.class);
+               
Mockito.when(mockReq.getHeader(X_CUSTOM_HEADER)).thenReturn("valueUnimportant");
+
+               // Objects to verify interactions based on request
+               HttpServletResponse mockRes = 
Mockito.mock(HttpServletResponse.class);
+               FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+               // Object under test
+               RangerCSRFPreventionFilter filter = new 
RangerCSRFPreventionFilter();
+               filter.doFilter(mockReq, mockRes, mockChain);
+
+               Mockito.verify(mockChain).doFilter(mockReq, mockRes);
+       }
+
+       @Test
+       public void testMissingHeaderWithCustomHeaderConfig_badRequest() throws 
ServletException, IOException {
+               // CSRF has not been sent
+               HttpServletRequest mockReq = 
Mockito.mock(HttpServletRequest.class);
+               
Mockito.when(mockReq.getHeader(X_CUSTOM_HEADER)).thenReturn(null);
+               
Mockito.when(mockReq.getHeader(RangerCSRFPreventionFilter.HEADER_USER_AGENT)).thenReturn(userAgent);
+
+               // Objects to verify interactions based on request
+               HttpServletResponse mockRes = 
Mockito.mock(HttpServletResponse.class);
+               FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+               // Object under test
+               RangerCSRFPreventionFilter filter = new 
RangerCSRFPreventionFilter();
+               filter.doFilter(mockReq, mockRes, mockChain);
+
+               Mockito.verifyZeroInteractions(mockChain);
+       }
+
+       @Test
+       public void testMissingHeaderIgnoreGETMethodConfig_goodRequest()
+                       throws ServletException, IOException {
+               // CSRF has not been sent
+               HttpServletRequest mockReq = 
Mockito.mock(HttpServletRequest.class);
+               
Mockito.when(mockReq.getHeader(RangerCSRFPreventionFilter.HEADER_DEFAULT)).thenReturn(null);
+               Mockito.when(mockReq.getMethod()).thenReturn("GET");
+               
Mockito.when(mockReq.getHeader(RangerCSRFPreventionFilter.HEADER_USER_AGENT)).thenReturn(userAgent);
+
+               // Objects to verify interactions based on request
+               HttpServletResponse mockRes = 
Mockito.mock(HttpServletResponse.class);
+               FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+               // Object under test
+               RangerCSRFPreventionFilter filter = new 
RangerCSRFPreventionFilter();
+               filter.doFilter(mockReq, mockRes, mockChain);
+
+               Mockito.verify(mockChain).doFilter(mockReq, mockRes);
+       }
+
+       @Test
+       public void testMissingHeaderMultipleIgnoreMethodsConfig_badRequest()
+                       throws ServletException, IOException {
+               // CSRF has not been sent
+               HttpServletRequest mockReq = 
Mockito.mock(HttpServletRequest.class);
+               
Mockito.when(mockReq.getHeader(RangerCSRFPreventionFilter.HEADER_DEFAULT))
+                               .thenReturn(null);
+               Mockito.when(mockReq.getMethod()).thenReturn("PUT");
+               
Mockito.when(mockReq.getHeader(RangerCSRFPreventionFilter.HEADER_USER_AGENT)).thenReturn(userAgent);
+
+               // Objects to verify interactions based on request
+               HttpServletResponse mockRes = 
Mockito.mock(HttpServletResponse.class);
+               FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+               // Object under test
+               RangerCSRFPreventionFilter filter = new 
RangerCSRFPreventionFilter();
+               filter.doFilter(mockReq, mockRes, mockChain);
+
+               Mockito.verifyZeroInteractions(mockChain);
+       }
+}
\ No newline at end of file

Reply via email to