atlas-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From shweth...@apache.org
Subject [2/4] incubator-atlas git commit: ATLAS-584 Integrate CSRF prevention filter (kevalbhatt18 via shwethags)
Date Thu, 07 Jul 2016 06:14:44 GMT
ATLAS-584 Integrate CSRF prevention filter (kevalbhatt18 via shwethags)


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

Branch: refs/heads/master
Commit: dda382f491f1bb26bdeb31620092a11ce9fdb710
Parents: 33fdad7
Author: Shwetha GS <sshivalingamurthy@hortonworks.com>
Authored: Thu Jul 7 10:31:02 2016 +0530
Committer: Shwetha GS <sshivalingamurthy@hortonworks.com>
Committed: Thu Jul 7 10:31:02 2016 +0530

----------------------------------------------------------------------
 .../public/js/collection/BaseCollection.js      |   4 +-
 dashboardv2/public/js/main.js                   |  16 +-
 dashboardv2/public/js/models/BaseModel.js       |   3 +-
 .../public/js/utils/CommonViewFunction.js       |  55 ++++-
 .../business_catalog/BusinessCatalogHeader.js   |  11 +-
 dashboardv2/public/js/views/site/Header.js      |  11 +-
 distro/src/conf/atlas-application.properties    |   8 +-
 release-log.txt                                 |   1 +
 .../web/filters/AtlasCSRFPreventionFilter.java  | 247 +++++++++++++++++++
 .../atlas/web/resources/AdminResource.java      |  11 +
 webapp/src/main/resources/spring-security.xml   |   4 +
 .../filters/AtlasCSRFPreventionFilterTest.java  | 149 +++++++++++
 12 files changed, 492 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/dashboardv2/public/js/collection/BaseCollection.js
----------------------------------------------------------------------
diff --git a/dashboardv2/public/js/collection/BaseCollection.js b/dashboardv2/public/js/collection/BaseCollection.js
index 0c148ac..e4ac1ae 100644
--- a/dashboardv2/public/js/collection/BaseCollection.js
+++ b/dashboardv2/public/js/collection/BaseCollection.js
@@ -19,8 +19,9 @@
 define(['require',
     'utils/Globals',
     'utils/Utils',
+    'utils/CommonViewFunction',
     'backbone.paginator'
-], function(require, Globals, Utils) {
+], function(require, Globals, Utils, CommonViewFunction) {
     'use strict';
 
     var BaseCollection = Backbone.PageableCollection.extend(
@@ -138,6 +139,7 @@ define(['require',
                 return retCols;
             },
             nonCrudOperation: function(url, requestMethod, options) {
+                options['beforeSend'] = CommonViewFunction.addRestCsrfCustomHeader;
                 return Backbone.sync.call(this, null, this, _.extend({
                     url: url,
                     type: requestMethod

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/dashboardv2/public/js/main.js
----------------------------------------------------------------------
diff --git a/dashboardv2/public/js/main.js b/dashboardv2/public/js/main.js
index ceed10c..552d906 100644
--- a/dashboardv2/public/js/main.js
+++ b/dashboardv2/public/js/main.js
@@ -140,11 +140,23 @@ require.config({
 
 require(['App',
     'router/Router',
+    'utils/CommonViewFunction',
+    'utils/Globals',
     'utils/Overrides',
     'bootstrap',
     'd3',
     'select2'
-], function(App, Router) {
+], function(App, Router, CommonViewFunction, Globals) {
     App.appRouter = new Router();
-    App.start();
+    CommonViewFunction.userDataFetch({
+        url: Globals.baseURL + "/api/atlas/admin/session",
+        callback: function(response) {
+            if (response && response.userName) {
+                Globals.userLogedIn.status = true;
+                Globals.userLogedIn.response = response;
+            }
+            App.start();
+        }
+    });
+
 });

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/dashboardv2/public/js/models/BaseModel.js
----------------------------------------------------------------------
diff --git a/dashboardv2/public/js/models/BaseModel.js b/dashboardv2/public/js/models/BaseModel.js
index da96d04..27e0332 100644
--- a/dashboardv2/public/js/models/BaseModel.js
+++ b/dashboardv2/public/js/models/BaseModel.js
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-define(['require', 'utils/Utils', 'backbone'], function(require, Utils, Backbone) {
+define(['require', 'utils/Utils', 'backbone','utils/CommonViewFunction'], function(require,
Utils, Backbone,CommonViewFunction) {
     'use strict';
 
     var BaseModel = Backbone.Model.extend(
@@ -60,6 +60,7 @@ define(['require', 'utils/Utils', 'backbone'], function(require, Utils,
Backbone
              * @return {[type]}               [description]
              */
             nonCrudOperation: function(url, requestMethod, options) {
+                options['beforeSend'] =  CommonViewFunction.addRestCsrfCustomHeader;
                 return Backbone.sync.call(this, null, this, _.extend({
                     url: url,
                     type: requestMethod

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/dashboardv2/public/js/utils/CommonViewFunction.js
----------------------------------------------------------------------
diff --git a/dashboardv2/public/js/utils/CommonViewFunction.js b/dashboardv2/public/js/utils/CommonViewFunction.js
index ed6a34f..6d88dc8 100644
--- a/dashboardv2/public/js/utils/CommonViewFunction.js
+++ b/dashboardv2/public/js/utils/CommonViewFunction.js
@@ -419,7 +419,6 @@ define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Glob
                 return "api/atlas/v1/entities/" + options.guid + "/tags/" + name;
             };
             VCatalog.save(null, {
-                beforeSend: function() {},
                 success: function(data) {
                     Utils.notifySuccess({
                         content: "Term " + name + Messages.addTermToEntitySuccessMessage
@@ -435,7 +434,7 @@ define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Glob
                     if (data && data.responseText) {
                         var data = JSON.parse(data.responseText);
                         Utils.notifyError({
-                            content: data.message
+                            content: data.message || data.msgDesc
                         });
                         if (options.callback) {
                             options.callback();
@@ -446,13 +445,63 @@ define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages',
'utils/Glob
             });
         })
     }
+    CommonViewFunction.addRestCsrfCustomHeader = function(xhr, settings) {
+        //    if (settings.url == null || !settings.url.startsWith('/webhdfs/')) {
+        if (settings.url == null) {
+            return;
+        }
+        var method = settings.type;
+        if (CommonViewFunction.restCsrfCustomHeader != null && !CommonViewFunction.restCsrfMethodsToIgnore[method])
{
+            // The value of the header is unimportant.  Only its presence matters.
+            xhr.setRequestHeader(CommonViewFunction.restCsrfCustomHeader, '""');
+        }
+    }
+    CommonViewFunction.restCsrfCustomHeader = null;
+    CommonViewFunction.restCsrfMethodsToIgnore = null;
     CommonViewFunction.userDataFetch = function(options) {
+        var csrfEnabled = false,
+            header = null,
+            methods = [];
+
+        function getTrimmedStringArrayValue(string) {
+            var str = string,
+                array = [];
+            if (str) {
+                var splitStr = str.split(',');
+                for (var i = 0; i < splitStr.length; i++) {
+                    array.push(splitStr[i].trim());
+                }
+            }
+            return array;
+        }
         if (options.url) {
             $.ajax({
                 url: options.url,
                 success: function(response) {
+                    if (response) {
+                        if (response['atlas.rest-csrf.enabled']) {
+                            var str = "" + response['atlas.rest-csrf.enabled'];
+                            csrfEnabled = (str.toLowerCase() == 'true');
+                        }
+                        if (response['atlas.rest-csrf.custom-header']) {
+                            header = response['atlas.rest-csrf.custom-header'].trim();
+                        }
+                        if (response['atlas.rest-csrf.methods-to-ignore']) {
+                            methods = getTrimmedStringArrayValue(response['atlas.rest-csrf.methods-to-ignore']);
+                        }
+                        if (csrfEnabled) {
+                            CommonViewFunction.restCsrfCustomHeader = header;
+                            CommonViewFunction.restCsrfMethodsToIgnore = {};
+                            methods.map(function(method) { CommonViewFunction.restCsrfMethodsToIgnore[method]
= true; });
+                            Backbone.$.ajaxSetup({
+                                beforeSend: CommonViewFunction.addRestCsrfCustomHeader
+                            });
+                        }
+                    }
+                },
+                complete: function(response) {
                     if (options.callback) {
-                        options.callback(response);
+                        options.callback(response.responseJSON);
                     }
                 }
             });

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/dashboardv2/public/js/views/business_catalog/BusinessCatalogHeader.js
----------------------------------------------------------------------
diff --git a/dashboardv2/public/js/views/business_catalog/BusinessCatalogHeader.js b/dashboardv2/public/js/views/business_catalog/BusinessCatalogHeader.js
index 8fa436c..6be1d2d 100644
--- a/dashboardv2/public/js/views/business_catalog/BusinessCatalogHeader.js
+++ b/dashboardv2/public/js/views/business_catalog/BusinessCatalogHeader.js
@@ -40,16 +40,7 @@ define(['require',
         render: function() {
             var that = this;
             $(this.el).html(this.template());
-            if (!Globals.userLogedIn.status) {
-                CommonViewFunction.userDataFetch({
-                    url: Globals.baseURL + "/api/atlas/admin/session",
-                    callback: function(response) {
-                        that.$('.userName').html(response.userName);
-                        Globals.userLogedIn.status = true;
-                        Globals.userLogedIn.response = response;
-                    }
-                });
-            } else {
+            if (Globals.userLogedIn.status) {
                 that.$('.userName').html(Globals.userLogedIn.response.userName);
             }
             var that = this;

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/dashboardv2/public/js/views/site/Header.js
----------------------------------------------------------------------
diff --git a/dashboardv2/public/js/views/site/Header.js b/dashboardv2/public/js/views/site/Header.js
index 467cbf5..f53d3e8 100644
--- a/dashboardv2/public/js/views/site/Header.js
+++ b/dashboardv2/public/js/views/site/Header.js
@@ -30,16 +30,7 @@ define(['require',
         initialize: function(options) {},
         onRender: function() {
             var that = this;
-            if (!Globals.userLogedIn.status) {
-                CommonViewFunction.userDataFetch({
-                    url: Globals.baseURL + "/api/atlas/admin/session",
-                    callback: function(response) {
-                        that.$('.userName').html(response.userName);
-                        Globals.userLogedIn.status = true;
-                        Globals.userLogedIn.response = response;
-                    }
-                });
-            } else {
+            if (Globals.userLogedIn.status) {
                 that.$('.userName').html(Globals.userLogedIn.response.userName);
             }
         },

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/distro/src/conf/atlas-application.properties
----------------------------------------------------------------------
diff --git a/distro/src/conf/atlas-application.properties b/distro/src/conf/atlas-application.properties
index 79a4982..215d8d5 100755
--- a/distro/src/conf/atlas-application.properties
+++ b/distro/src/conf/atlas-application.properties
@@ -178,4 +178,10 @@ atlas.authorizer.impl=SIMPLE
 #atlas.graph.storage.cache.db-cache-time=120000
 
 #########  Business Catalog  #########
-atlas.taxonomy.default.name=Catalog
\ No newline at end of file
+atlas.taxonomy.default.name=Catalog
+
+#########  CSRF Configs  #########
+atlas.rest-csrf.enabled=true
+atlas.rest-csrf.browser-useragents-regex=^Mozilla.*,^Opera.*,^Chrome.*
+atlas.rest-csrf.methods-to-ignore=GET,OPTIONS,HEAD,TRACE
+atlas.rest-csrf.custom-header=X-XSRF-HEADER

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/release-log.txt
----------------------------------------------------------------------
diff --git a/release-log.txt b/release-log.txt
index 20a8a2c..78ae9a2 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -6,6 +6,7 @@ INCOMPATIBLE CHANGES:
 
 
 ALL CHANGES:
+ATLAS-584 Integrate CSRF prevention filter (kevalbhatt18 via shwethags)
 ATLAS-963 UI: Entity details is not display String array attribute values correctly (kevalbhatt18
via shwethags)
 ATLAS-988 HiveHookIT.testInsertIntoTable is broken (svimal2106 via shwethags)
 ATLAS-655 Please delete old releases from mirroring system (shwethags)

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java
new file mode 100644
index 0000000..3cc83c5
--- /dev/null
+++ b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java
@@ -0,0 +1,247 @@
+/**
+ * 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.atlas.web.filters;
+
+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.atlas.ApplicationProperties;
+import org.apache.atlas.AtlasException;
+import org.apache.commons.configuration.Configuration;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.json.simple.JSONObject;
+
+public class AtlasCSRFPreventionFilter implements Filter {
+	private static final Logger LOG = Logger.getLogger(AtlasCSRFPreventionFilter.class);
+	private static Configuration configuration;
+	
+	static {
+	    try {
+            configuration  = ApplicationProperties.get();
+            LOG.info("Configuration obtained :: "+configuration);
+        } catch (AtlasException e) {
+            LOG.error(e.getMessage(), e);
+        }
+	}
+	
+	public static final boolean isCSRF_ENABLED = configuration.getBoolean("atlas.rest-csrf.enabled",
true);
+	public static final String BROWSER_USER_AGENT_PARAM = "atlas.rest-csrf.browser-useragents-regex";
+	public static final String BROWSER_USER_AGENTS_DEFAULT = "^Mozilla.*,^Opera.*,^Chrome";
+	public static final String CUSTOM_METHODS_TO_IGNORE_PARAM = "atlas.rest-csrf.methods-to-ignore";
+	public static final String METHODS_TO_IGNORE_DEFAULT = "GET,OPTIONS,HEAD,TRACE";
+	public static final String CUSTOM_HEADER_PARAM = "atlas.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 AtlasCSRFPreventionFilter() {
+		try {
+			if (isCSRF_ENABLED){
+				init(null);
+			}
+		} catch (Exception e) {
+			LOG.error("Error while initializing Filter ", e);
+		}
+	}
+	
+	public void init(FilterConfig filterConfig) throws ServletException {
+		String customHeader = configuration.getString(CUSTOM_HEADER_PARAM, HEADER_DEFAULT);
+	    if (customHeader != null) {
+	      headerName = customHeader;
+	    }
+	    
+	    String customMethodsToIgnore = configuration.getString(CUSTOM_METHODS_TO_IGNORE_PARAM,
METHODS_TO_IGNORE_DEFAULT);
+        if (customMethodsToIgnore != null) {
+          parseMethodsToIgnore(customMethodsToIgnore);
+        } else {
+          parseMethodsToIgnore(METHODS_TO_IGNORE_DEFAULT);
+        }
+        String agents = configuration.getString(BROWSER_USER_AGENT_PARAM, BROWSER_USER_AGENTS_DEFAULT);
+        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 {
+			JSONObject json = new JSONObject();
+            ObjectMapper mapper = new ObjectMapper();
+            json.put("msgDesc", message);
+            String jsonAsStr = mapper.writeValueAsString(json);
+            httpResponse.setContentType("application/json");
+            httpResponse.setStatus(code);
+            httpResponse.setCharacterEncoding("UTF-8");
+            httpResponse.getWriter().write(jsonAsStr);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java
index 3a46068..b7f6cf2 100755
--- a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java
+++ b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java
@@ -31,6 +31,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
 import org.apache.atlas.AtlasClient;
+import org.apache.atlas.web.filters.AtlasCSRFPreventionFilter;
 import org.apache.atlas.web.service.ServiceState;
 import org.apache.atlas.web.util.Servlets;
 import org.apache.commons.configuration.ConfigurationException;
@@ -51,6 +52,11 @@ import com.google.inject.Inject;
 @Singleton
 public class AdminResource {
 
+    private static final String isCSRF_ENABLED = "atlas.rest-csrf.enabled";
+    private static final String BROWSER_USER_AGENT_PARAM = "atlas.rest-csrf.browser-useragents-regex";
+    private static final String CUSTOM_METHODS_TO_IGNORE_PARAM = "atlas.rest-csrf.methods-to-ignore";
+    private static final String CUSTOM_HEADER_PARAM = "atlas.rest-csrf.custom-header";
+    
     private Response version;
     private ServiceState serviceState;
 
@@ -147,6 +153,11 @@ public class AdminResource {
                 }
             } 
             
+            responseData.put(isCSRF_ENABLED,  AtlasCSRFPreventionFilter.isCSRF_ENABLED);
+            responseData.put(BROWSER_USER_AGENT_PARAM, AtlasCSRFPreventionFilter.BROWSER_USER_AGENTS_DEFAULT);
+            responseData.put(CUSTOM_METHODS_TO_IGNORE_PARAM, AtlasCSRFPreventionFilter.METHODS_TO_IGNORE_DEFAULT);
+            responseData.put(CUSTOM_HEADER_PARAM, AtlasCSRFPreventionFilter.HEADER_DEFAULT);
+            
             responseData.put("userName", userName);
             responseData.put("groups", groups);
             Response response = Response.ok(responseData).build();

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/webapp/src/main/resources/spring-security.xml
----------------------------------------------------------------------
diff --git a/webapp/src/main/resources/spring-security.xml b/webapp/src/main/resources/spring-security.xml
index c21a644..ea9aa94 100644
--- a/webapp/src/main/resources/spring-security.xml
+++ b/webapp/src/main/resources/spring-security.xml
@@ -43,6 +43,7 @@
         <intercept-url pattern="/**" access="isAuthenticated()" />
 
         <security:custom-filter ref="krbAuthenticationFilter" after="SERVLET_API_SUPPORT_FILTER"
/>
+        <security:custom-filter ref="CSRFPreventionFilter" after="REMEMBER_ME_FILTER"
/>
 
         <form-login
                 login-page="/login.jsp"
@@ -59,6 +60,9 @@
 
     <beans:bean id="krbAuthenticationFilter" class="org.apache.atlas.web.filters.AtlasAuthenticationFilter">
     </beans:bean>
+    
+    <beans:bean id="CSRFPreventionFilter" class="org.apache.atlas.web.filters.AtlasCSRFPreventionFilter">
+    </beans:bean>
 
     <beans:bean id="atlasAuthenticationSuccessHandler"
                 class="org.apache.atlas.web.security.AtlasAuthenticationSuccessHandler" />

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/dda382f4/webapp/src/test/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilterTest.java
----------------------------------------------------------------------
diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilterTest.java
b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilterTest.java
new file mode 100644
index 0000000..a742dd5
--- /dev/null
+++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilterTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.atlas.web.filters;
+
+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 AtlasCSRFPreventionFilterTest {
+	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(AtlasCSRFPreventionFilter.HEADER_DEFAULT)).thenReturn(null);
+		Mockito.when(mockReq.getHeader(AtlasCSRFPreventionFilter.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
+		AtlasCSRFPreventionFilter filter = new AtlasCSRFPreventionFilter();
+		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(AtlasCSRFPreventionFilter.HEADER_DEFAULT)).thenReturn("valueUnimportant");
+		Mockito.when(mockReq.getHeader(AtlasCSRFPreventionFilter.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
+		AtlasCSRFPreventionFilter filter = new AtlasCSRFPreventionFilter();
+		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
+		AtlasCSRFPreventionFilter filter = new AtlasCSRFPreventionFilter();
+		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(AtlasCSRFPreventionFilter.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
+		AtlasCSRFPreventionFilter filter = new AtlasCSRFPreventionFilter();
+		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(AtlasCSRFPreventionFilter.HEADER_DEFAULT)).thenReturn(null);
+		Mockito.when(mockReq.getMethod()).thenReturn("GET");
+		Mockito.when(mockReq.getHeader(AtlasCSRFPreventionFilter.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
+		AtlasCSRFPreventionFilter filter = new AtlasCSRFPreventionFilter();
+		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(AtlasCSRFPreventionFilter.HEADER_DEFAULT))
+				.thenReturn(null);
+		Mockito.when(mockReq.getMethod()).thenReturn("PUT");
+		Mockito.when(mockReq.getHeader(AtlasCSRFPreventionFilter.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
+		AtlasCSRFPreventionFilter filter = new AtlasCSRFPreventionFilter();
+		filter.doFilter(mockReq, mockRes, mockChain);
+
+		Mockito.verifyZeroInteractions(mockChain);
+	}
+}


Mime
View raw message