You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ga...@apache.org on 2016/05/30 12:28:50 UTC
[2/4] incubator-ranger git commit: RANGER-995 : CSRF implementation
in Ranger
RANGER-995 : CSRF implementation in Ranger
Signed-off-by: Gautam Borad <ga...@apache.org>
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 <an...@freestoneinfotech.com>
Authored: Wed May 25 12:19:42 2016 +0530
Committer: Gautam Borad <ga...@apache.org>
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