This is an automated email from the ASF dual-hosted git repository. amestry pushed a commit to branch branch-2.0 in repository https://gitbox.apache.org/repos/asf/atlas.git
commit be5d557a0038536733730ca47ff473d876415c3f Author: nixonrodrigues <ni...@apache.org> AuthorDate: Mon Aug 23 17:27:18 2021 +0530 ATLAS-4379 : Atlas Filter changes for user inactivity on Atlas UI --- .../java/org/apache/atlas/AtlasConfiguration.java | 4 +- .../web/filters/AtlasAuthenticationFilter.java | 48 ++++++++- .../org/apache/atlas/web/filters/RestUtil.java | 108 +++++++++++++++++++++ .../apache/atlas/web/resources/AdminResource.java | 1 + .../AtlasAuthenticationSuccessHandler.java | 16 ++- 5 files changed, 170 insertions(+), 7 deletions(-) diff --git a/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java b/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java index 2f2c8a5..fa519ef 100644 --- a/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java +++ b/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java @@ -80,7 +80,9 @@ public enum AtlasConfiguration { DSL_EXECUTOR_TRAVERSAL("atlas.dsl.executor.traversal", true), DSL_CACHED_TRANSLATOR("atlas.dsl.cached.translator", true), DEBUG_METRICS_ENABLED("atlas.debug.metrics.enabled", false), - TASKS_USE_ENABLED("atlas.tasks.enabled", true); + TASKS_USE_ENABLED("atlas.tasks.enabled", true), + SESSION_TIMEOUT_SECS("atlas.session.timeout.secs", 3600); + private static final Configuration APPLICATION_PROPERTIES; diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java index d9b1c82..b8d21b9 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java +++ b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java @@ -19,6 +19,7 @@ package org.apache.atlas.web.filters; import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasConfiguration; import org.apache.atlas.security.SecurityProperties; import org.apache.atlas.utils.AuthenticationUtil; import org.apache.atlas.web.security.AtlasAuthenticationProvider; @@ -73,6 +74,9 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.hadoop.security.authorize.AuthorizationException; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; + +import static org.apache.atlas.web.filters.RestUtil.constructForwardableURL; /** @@ -90,6 +94,7 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { private static final String[] DEFAULT_PROXY_USERS = new String[] { "knox" }; private static final String CONF_PROXYUSER_PREFIX = "atlas.proxyuser"; protected static final ServletContext nullContext = new NullServletContext(); + private static final String ORIGINAL_URL_QUERY_PARAM = "originalUrl"; private Signer signer; private SignerSecretProvider secretProvider; @@ -102,8 +107,9 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { private Set<String> atlasProxyUsers = new HashSet<>(); private HttpServlet optionsServlet; private boolean supportTrustedProxy = false; + private int sessionTimeout; - + private SecurityContextLogoutHandler logoutHandler; public AtlasAuthenticationFilter() { LOG.info("==> AtlasAuthenticationFilter()"); @@ -193,7 +199,7 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { optionsServlet = new HttpServlet() { }; optionsServlet.init(); - + logoutHandler = new SecurityContextLogoutHandler(); LOG.info("<== AtlasAuthenticationFilter.init(filterConfig={})", filterConfig); } @@ -301,6 +307,10 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { LOG.debug(" AuthenticationFilterConfig: {}", ret); + sessionTimeout = AtlasConfiguration.SESSION_TIMEOUT_SECS.getInt(); + if(sessionTimeout < 30){ + LOG.warn("AtlasAuthenticationFilter:: sessionTimeout is set low"); + } supportKeyTabBrowserLogin = configuration.getBoolean("atlas.authentication.method.kerberos.support.keytab.browser.login", false); supportTrustedProxy = configuration.getBoolean("atlas.authentication.method.trustedproxy", true); String agents = configuration.getString(AtlasCSRFPreventionFilter.BROWSER_USER_AGENT_PARAM, AtlasCSRFPreventionFilter.BROWSER_USER_AGENTS_DEFAULT); @@ -332,6 +342,8 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); HttpServletResponse httpResponse = (HttpServletResponse) response; AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse); + String action = httpRequest.getParameter("action"); + String doAsUser = request.getParameter("doAs"); HeadersUtil.setHeaderMapAttributes(responseWrapper, HeadersUtil.X_FRAME_OPTIONS_KEY); HeadersUtil.setHeaderMapAttributes(responseWrapper, HeadersUtil.X_CONTENT_TYPE_OPTIONS_KEY); @@ -344,6 +356,14 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { } } + if (supportTrustedProxy && StringUtils.isNotEmpty(doAsUser) && StringUtils.equals(action, RestUtil.TIMEOUT_ACTION)) { + if (existingAuth != null) { + logoutHandler.logout(httpRequest, httpResponse, existingAuth); + } + redirectTimeoutReqeust(httpRequest, httpResponse); + return; + } + if (existingAuth == null) { String authHeader = httpRequest.getHeader("Authorization"); @@ -365,6 +385,28 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { } } + private void redirectTimeoutReqeust(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException{ + String logoutUrl = httpRequest.getRequestURL().toString(); + + logoutUrl = StringUtils.replace(logoutUrl, httpRequest.getRequestURI(), RestUtil.LOGOUT_URL); + if (LOG.isDebugEnabled()) { + LOG.debug("logoutUrl value is " + logoutUrl); + } + String xForwardedURL = constructForwardableURL(httpRequest); + + + if (LOG.isDebugEnabled()) { + LOG.debug("xForwardedURL = " + xForwardedURL); + } + String redirectUrl = RestUtil.constructRedirectURL(httpRequest, logoutUrl, xForwardedURL, ORIGINAL_URL_QUERY_PARAM); + + if (LOG.isDebugEnabled()) { + LOG.debug("Redirect URL = " + redirectUrl); + LOG.debug("session id = " + httpRequest.getRequestedSessionId()); + } + + httpResponse.sendRedirect(redirectUrl); + } /** @@ -717,7 +759,7 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ((AbstractAuthenticationToken) finalAuthentication).setDetails(webDetails); SecurityContextHolder.getContext().setAuthentication(finalAuthentication); - + httpRequest.getSession().setMaxInactiveInterval(sessionTimeout); request.setAttribute("atlas.http.authentication.type", true); if (!StringUtils.equals(loggedInUser, userName)) { diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/RestUtil.java b/webapp/src/main/java/org/apache/atlas/web/filters/RestUtil.java new file mode 100644 index 0000000..01b64d7 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/filters/RestUtil.java @@ -0,0 +1,108 @@ +/** + * 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 org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletRequest; +import java.util.Enumeration; + +public class RestUtil { + + private static final Logger LOG = LoggerFactory.getLogger(RestUtil.class); + public static final String TIMEOUT_ACTION = "timeout"; + public static final String LOGOUT_URL = "/logout.html"; + private static final String PROXY_ATLAS_URL_PATH = "/atlas"; + private static final String X_FORWARDED_PROTO = "x-forwarded-proto"; + private static final String X_FORWARDED_HOST = "x-forwarded-host"; + private static final String X_FORWARDED_CONTEXT = "x-forwarded-context"; + public static final String DELIMITTER = "://"; + + public static String constructForwardableURL(HttpServletRequest httpRequest) { + String xForwardedProto = ""; + String xForwardedHost = ""; + String xForwardedContext = ""; + Enumeration<?> headerNames = httpRequest.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String name = (String) headerNames.nextElement(); + Enumeration<?> values = httpRequest.getHeaders(name); + String value = ""; + if (values != null) { + while (values.hasMoreElements()) { + value = (String) values.nextElement(); + } + } + if (StringUtils.trimToNull(name) != null && StringUtils.trimToNull(value) != null) { + if (name.equalsIgnoreCase(X_FORWARDED_PROTO)) { + xForwardedProto = value; + } else if (name.equalsIgnoreCase(X_FORWARDED_HOST)) { + xForwardedHost = value; + } else if (name.equalsIgnoreCase(X_FORWARDED_CONTEXT)) { + xForwardedContext = value; + } + } + } + if (xForwardedHost.contains(",")) { + if (LOG.isDebugEnabled()) { + LOG.debug("xForwardedHost value is " + xForwardedHost + " it contains multiple hosts, selecting the first host."); + } + xForwardedHost = xForwardedHost.split(",")[0].trim(); + } + String xForwardedURL = ""; + if (StringUtils.trimToNull(xForwardedProto) != null) { + //if header contains x-forwarded-host and x-forwarded-context + if (StringUtils.trimToNull(xForwardedHost) != null && StringUtils.trimToNull(xForwardedContext) != null) { + xForwardedURL = xForwardedProto + DELIMITTER + xForwardedHost + xForwardedContext + PROXY_ATLAS_URL_PATH + httpRequest.getRequestURI(); + } else if (StringUtils.trimToNull(xForwardedHost) != null) { + //if header contains x-forwarded-host and does not contains x-forwarded-context + xForwardedURL = xForwardedProto + DELIMITTER + xForwardedHost + httpRequest.getRequestURI(); + } else { + //if header does not contains x-forwarded-host and x-forwarded-context + //preserve the x-forwarded-proto value coming from the request. + String requestURL = httpRequest.getRequestURL().toString(); + if (StringUtils.trimToNull(requestURL) != null && requestURL.startsWith("http:")) { + requestURL = requestURL.replaceFirst("http", xForwardedProto); + } + xForwardedURL = requestURL; + } + } + return xForwardedURL; + } + + public static String constructRedirectURL(HttpServletRequest request, String redirectUrl, String xForwardedURL, String originalUrlQueryParam) { + String delimiter = "?"; + if (redirectUrl.contains("?")) { + delimiter = "&"; + } + String loginURL = redirectUrl + delimiter + originalUrlQueryParam + "="; + if (StringUtils.trimToNull(xForwardedURL) != null) { + loginURL += xForwardedURL; + } else { + loginURL += request.getRequestURL().append(getOriginalQueryString(request)); + } + return loginURL; + } + + private static String getOriginalQueryString(HttpServletRequest request) { + String originalQueryString = request.getQueryString(); + return (originalQueryString == null) ? "" : "?" + originalQueryString; + } +} 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 01fb8ec..baa040f 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 @@ -370,6 +370,7 @@ public class AdminResource { responseData.put(UI_DATE_FORMAT, uiDateFormat); responseData.put(AtlasConfiguration.DEBUG_METRICS_ENABLED.getPropertyName(), isDebugMetricsEnabled); responseData.put(AtlasConfiguration.TASKS_USE_ENABLED.getPropertyName(), isTasksEnabled); + responseData.put(AtlasConfiguration.SESSION_TIMEOUT_SECS.getPropertyName(), AtlasConfiguration.SESSION_TIMEOUT_SECS.getInt()); String salt = (String) request.getSession().getAttribute(CSRF_TOKEN); if (StringUtils.isEmpty(salt)) { diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java index e7a5d66..1b1a808 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java @@ -18,6 +18,7 @@ package org.apache.atlas.web.security; +import org.apache.atlas.AtlasConfiguration; import org.json.simple.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,6 +26,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -35,6 +37,14 @@ import java.io.IOException; public class AtlasAuthenticationSuccessHandler implements AuthenticationSuccessHandler { private static Logger LOG = LoggerFactory.getLogger(AuthenticationSuccessHandler.class); + private int sessionTimeout = 3600; + public static final String LOCALLOGIN = "locallogin"; + + @PostConstruct + public void setup() { + sessionTimeout = AtlasConfiguration.SESSION_TIMEOUT_SECS.getInt(); + } + @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { @@ -45,10 +55,10 @@ public class AtlasAuthenticationSuccessHandler implements AuthenticationSuccessH json.put("msgDesc", "Success"); if (request.getSession() != null) { // incase of form based login mark it as local login in session - request.getSession().setAttribute("locallogin","true"); - request.getServletContext().setAttribute(request.getSession().getId(), "locallogin"); + request.getSession().setAttribute(LOCALLOGIN,"true"); + request.getServletContext().setAttribute(request.getSession().getId(), LOCALLOGIN); + request.getSession().setMaxInactiveInterval(sessionTimeout); } - response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); response.setCharacterEncoding("UTF-8");