Repository: incubator-atlas Updated Branches: refs/heads/master ae92406d9 -> 854b79280
ATLAS-1244: added support for KnoxSSO Authentication Signed-off-by: Madhan Neethiraj <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/incubator-atlas/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-atlas/commit/854b7928 Tree: http://git-wip-us.apache.org/repos/asf/incubator-atlas/tree/854b7928 Diff: http://git-wip-us.apache.org/repos/asf/incubator-atlas/diff/854b7928 Branch: refs/heads/master Commit: 854b792802f9a56a47bb7bb461077921867bd967 Parents: ae92406 Author: nixonrodrigues <[email protected]> Authored: Wed Nov 2 16:47:59 2016 +0530 Committer: Madhan Neethiraj <[email protected]> Committed: Tue Nov 15 17:46:47 2016 -0800 ---------------------------------------------------------------------- distro/src/conf/atlas-application.properties | 9 + release-log.txt | 1 + webapp/pom.xml | 12 + .../web/filters/AtlasAuthenticationFilter.java | 2 +- .../AtlasKnoxSSOAuthenticationFilter.java | 428 +++++++++++++++++++ .../atlas/web/filters/SSOAuthentication.java | 74 ++++ .../filters/SSOAuthenticationProperties.java | 78 ++++ .../AtlasAbstractAuthenticationProvider.java | 17 +- .../security/AtlasAuthenticationProvider.java | 45 +- .../AtlasAuthenticationSuccessHandler.java | 5 + webapp/src/main/resources/spring-security.xml | 8 +- webapp/src/main/webapp/WEB-INF/web.xml | 9 + webapp/src/test/webapp/WEB-INF/web.xml | 9 + 13 files changed, 680 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/distro/src/conf/atlas-application.properties ---------------------------------------------------------------------- diff --git a/distro/src/conf/atlas-application.properties b/distro/src/conf/atlas-application.properties index 0349ccc..eea46a6 100755 --- a/distro/src/conf/atlas-application.properties +++ b/distro/src/conf/atlas-application.properties @@ -198,3 +198,12 @@ atlas.rest-csrf.custom-header=X-XSRF-HEADER ######### Enable Taxonomy ######### atlas.feature.taxonomy.enable=true + +############ KNOX Configs ################ +#atlas.sso.knox.browser.useragent=Mozilla,Chrome,Opera +#atlas.sso.knox.enabled=true +#atlas.sso.knox.providerurl=https://<knox gateway ip>:8443/gateway/knoxsso/api/v1/websso +#atlas.sso.knox.publicKey= + + + http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/release-log.txt ---------------------------------------------------------------------- diff --git a/release-log.txt b/release-log.txt index 80391f4..0649f09 100644 --- a/release-log.txt +++ b/release-log.txt @@ -9,6 +9,7 @@ ATLAS-1060 Add composite indexes for exact match performance improvements for al ATLAS-1127 Modify creation and modification timestamps to Date instead of Long(sumasai) ALL CHANGES: +ATLAS-1244 added support for KnoxSSO Authentication ATLAS-1295 Build failure due to patch for ATLAS-1081 (apoorvnaik via sumasai) ATLAS-1081 Atlas jetty server configuration (shwethags) ATLAS-1257 Map Entity REST APIs to ATLAS v1 backend (sumasai) http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/webapp/pom.xml ---------------------------------------------------------------------- diff --git a/webapp/pom.xml b/webapp/pom.xml index b9e1111..594b602 100755 --- a/webapp/pom.xml +++ b/webapp/pom.xml @@ -355,6 +355,18 @@ <scope>test</scope> </dependency> + <dependency> + <groupId>com.nimbusds</groupId> + <artifactId>nimbus-jose-jwt</artifactId> + <version>3.9</version> + <scope>compile</scope> + <exclusions> + <exclusion> + <groupId>org.bouncycastle</groupId> + <artifactId>bcprov-jdk15on</artifactId> + </exclusion> + </exclusions> + </dependency> </dependencies> <build> http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java ---------------------------------------------------------------------- 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 30200b5..3307015 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 @@ -435,7 +435,7 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { Collection<String> headerNames = httpResponse.getHeaderNames(); for (String headerName : headerNames) { String value = httpResponse.getHeader(headerName); - if (headerName.equalsIgnoreCase("Set-Cookie") && value.startsWith("JSESSIONID")) { + if (headerName.equalsIgnoreCase("Set-Cookie") && value.startsWith("ATLASSESSIONID")) { chk = false; break; } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java new file mode 100644 index 0000000..75a884d --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java @@ -0,0 +1,428 @@ + +/* + * 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 com.google.inject.Inject; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSObject; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.RSASSAVerifier; +import com.nimbusds.jwt.SignedJWT; +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.web.security.AtlasAuthenticationProvider; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetails; +import javax.servlet.*; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPublicKey; +import java.text.ParseException; +import java.util.Date; +import java.util.List; + + +public class AtlasKnoxSSOAuthenticationFilter implements Filter { + private static final Logger LOG = LoggerFactory.getLogger(AtlasKnoxSSOAuthenticationFilter.class); + + public static final String BROWSER_USERAGENT = "atlas.sso.knox.browser.useragent"; + public static final String JWT_AUTH_PROVIDER_URL = "atlas.sso.knox.providerurl"; + public static final String JWT_PUBLIC_KEY = "atlas.sso.knox.publicKey"; + public static final String JWT_COOKIE_NAME = "atlas.sso.knox.cookiename"; + public static final String JWT_ORIGINAL_URL_QUERY_PARAM = "atlas.sso.knox.query.param.originalurl"; + public static final String JWT_COOKIE_NAME_DEFAULT = "hadoop-jwt"; + public static final String JWT_ORIGINAL_URL_QUERY_PARAM_DEFAULT = "originalUrl"; + + private SSOAuthenticationProperties jwtProperties; + + private String originalUrlQueryParam = "originalUrl"; + private String authenticationProviderUrl = null; + private RSAPublicKey publicKey = null; + private String cookieName = "hadoop-jwt"; + private Configuration configuration = null; + private boolean ssoEnabled = false; + private JWSVerifier verifier = null; + + @Inject + public AtlasKnoxSSOAuthenticationFilter() { + try { + configuration = ApplicationProperties.get(); + } catch (Exception e) { + LOG.error("Error while getting application properties", e); + } + ssoEnabled = configuration.getBoolean("atlas.sso.knox.enabled", false); + jwtProperties = loadJwtProperties(); + setJwtProperties(); + } + + public AtlasKnoxSSOAuthenticationFilter( + SSOAuthenticationProperties jwtProperties) { + this.jwtProperties = jwtProperties; + setJwtProperties(); + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + /* + * doFilter of AtlasKnoxSSOAuthenticationFilter is the first in the filter list so in this it check for the request + * if the request is from browser and sso is enabled then it process the request against knox sso + * else if it's ssoenable and the request is with local login string then it show's the appropriate msg + * else if ssoenable is false then it contiunes with further filters as it was before sso + */ + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + + if (!ssoEnabled) { + filterChain.doFilter(servletRequest, servletResponse); + return; + } + + HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; + + if (LOG.isDebugEnabled()) { + LOG.debug("Knox doFilter {}", httpRequest.getRequestURI()); + } + + if (httpRequest.getSession() != null && httpRequest.getSession().getAttribute("locallogin") != null) { + servletRequest.setAttribute("ssoEnabled", false); + filterChain.doFilter(servletRequest, servletResponse); + return; + } + + if (!isWebUserAgent(httpRequest.getHeader("User-Agent")) || jwtProperties == null || isAuthenticated()) { + filterChain.doFilter(servletRequest, servletResponse); + return; + } + + if (LOG.isDebugEnabled()) { + LOG.debug("Knox ssoEnabled {} {}", ssoEnabled, httpRequest.getRequestURI()); + } + //if jwt properties are loaded and is current not authenticated then it will go for sso authentication + //Note : Need to remove !isAuthenticated() after knoxsso solve the bug from cross-origin script + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + String serializedJWT = getJWTFromCookie(httpRequest); + // if we get the hadoop-jwt token from the cookies then will process it further + if (serializedJWT != null) { + SignedJWT jwtToken = null; + try { + jwtToken = SignedJWT.parse(serializedJWT); + boolean valid = validateToken(jwtToken); + //if the public key provide is correct and also token is not expired the process token + if (valid) { + String userName = jwtToken.getJWTClaimsSet().getSubject(); + LOG.info("SSO login user : {} ", userName); + //if we get the userName from the token then log into atlas using the same user + if (userName != null && !userName.trim().isEmpty()) { + List<GrantedAuthority> grantedAuths = AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); + final UserDetails principal = new User(userName, "", grantedAuths); + final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken(principal, "", grantedAuths); + WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest); + ((AbstractAuthenticationToken) finalAuthentication).setDetails(webDetails); + AtlasAuthenticationProvider authenticationProvider = new AtlasAuthenticationProvider(); + authenticationProvider.setSsoEnabled(ssoEnabled); + Authentication authentication = authenticationProvider.authenticate(finalAuthentication); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + filterChain.doFilter(servletRequest, httpServletResponse); + } else { // if the token is not valid then redirect to knox sso + String ssourl = constructLoginURL(httpRequest); + if (LOG.isDebugEnabled()) { + LOG.debug("SSO URL ={} invalid", ssourl); + } + httpServletResponse.sendRedirect(ssourl); + } + } catch (ParseException e) { + LOG.warn("Unable to parse the JWT token", e); + } + } else { + String ssourl = constructLoginURL(httpRequest); + if (LOG.isDebugEnabled()) { + LOG.debug("SSO URL = {} serializedJWT null", ssourl); + } + httpServletResponse.sendRedirect(ssourl); + } + + } + + + private boolean isWebUserAgent(String userAgent) { + boolean isWeb = false; + if (jwtProperties != null) { + String userAgentList[] = jwtProperties.getUserAgentList(); + if (userAgentList != null && userAgentList.length > 0) { + for (String ua : userAgentList) { + if (StringUtils.startsWithIgnoreCase(userAgent, ua)) { + isWeb = true; + break; + } + } + } + } + return isWeb; + } + + + private void setJwtProperties() { + if (jwtProperties != null) { + authenticationProviderUrl = jwtProperties.getAuthenticationProviderUrl(); + publicKey = jwtProperties.getPublicKey(); + cookieName = jwtProperties.getCookieName(); + originalUrlQueryParam = jwtProperties.getOriginalUrlQueryParam(); + if (publicKey != null) { + verifier = new RSASSAVerifier(publicKey); + } + } + } + + /** + * Do not try to validate JWT if user already authenticated via other + * provider + * + * @return true, if JWT validation required + */ + private boolean isAuthenticated() { + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + return !(!(existingAuth != null && existingAuth.isAuthenticated()) || existingAuth instanceof SSOAuthentication); + } + + /** + * Encapsulate the acquisition of the JWT token from HTTP cookies within the + * request. + * + * @param req servlet request to get the JWT token from + * @return serialized JWT token + */ + protected String getJWTFromCookie(HttpServletRequest req) { + String serializedJWT = null; + Cookie[] cookies = req.getCookies(); + if (cookieName != null && cookies != null) { + for (Cookie cookie : cookies) { + if (cookieName.equals(cookie.getName())) { + if (LOG.isDebugEnabled()) { + LOG.debug(cookieName + " cookie has been found and is being processed"); + } + serializedJWT = cookie.getValue(); + break; + } + } + } + return serializedJWT; + } + + /** + * Create the URL to be used for authentication of the user in the absence + * of a JWT token within the incoming request. + * + * @param request for getting the original request URL + * @return url to use as login url for redirect + */ + protected String constructLoginURL(HttpServletRequest request) { + String delimiter = "?"; + if (authenticationProviderUrl.contains("?")) { + delimiter = "&"; + } + StringBuilder loginURL = new StringBuilder(); + loginURL.append(authenticationProviderUrl).append(delimiter).append(originalUrlQueryParam).append("=").append(request.getRequestURL().append(getOriginalQueryString(request))); + return loginURL.toString(); + } + + private String getOriginalQueryString(HttpServletRequest request) { + String originalQueryString = request.getQueryString(); + return (originalQueryString == null) ? "" : "?" + originalQueryString; + } + + /** + * This method provides a single method for validating the JWT for use in + * request processing. It provides for the override of specific aspects of + * this implementation through submethods used within but also allows for + * the override of the entire token validation algorithm. + * + * @param jwtToken the token to validate + * @return true if valid + */ + protected boolean validateToken(SignedJWT jwtToken) { + boolean isValid = validateSignature(jwtToken); + + if (isValid) { + isValid = validateExpiration(jwtToken); + if (!isValid) { + LOG.warn("Expiration time validation of JWT token failed."); + } + } else { + LOG.warn("Signature of JWT token could not be verified. Please check the public key"); + } + return isValid; + } + + /** + * Verify the signature of the JWT token in this method. This method depends + * on the public key that was established during init based upon the + * provisioned public key. Override this method in subclasses in order to + * customize the signature verification behavior. + * + * @param jwtToken the token that contains the signature to be validated + * @return valid true if signature verifies successfully; false otherwise + */ + protected boolean validateSignature(SignedJWT jwtToken) { + boolean valid = false; + if (JWSObject.State.SIGNED == jwtToken.getState()) { + if (LOG.isDebugEnabled()) { + LOG.debug("SSO token is in a SIGNED state"); + } + if (jwtToken.getSignature() != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("SSO token signature is not null"); + } + try { + if (verifier != null && jwtToken.verify(verifier)) { + valid = true; + if (LOG.isDebugEnabled()) { + LOG.debug("SSO token has been successfully verified"); + } + } else { + LOG.warn("SSO signature verification failed.Please check the public key"); + } + } catch (JOSEException je) { + LOG.warn("Error while validating signature", je); + } catch (Exception e) { + LOG.warn("Error while validating signature", e); + } + } + } + return valid; + } + + /** + * Validate that the expiration time of the JWT token has not been violated. + * If it has then throw an AuthenticationException. Override this method in + * subclasses in order to customize the expiration validation behavior. + * + * @param jwtToken the token that contains the expiration date to validate + * @return valid true if the token has not expired; false otherwise + */ + protected boolean validateExpiration(SignedJWT jwtToken) { + boolean valid = false; + try { + Date expires = jwtToken.getJWTClaimsSet().getExpirationTime(); + if (expires == null || new Date().before(expires)) { + if (LOG.isDebugEnabled()) { + LOG.debug("SSO token expiration date has been successfully validated"); + } + valid = true; + } else { + LOG.warn("SSO expiration date validation failed."); + } + } catch (ParseException pe) { + LOG.warn("SSO expiration date validation failed.", pe); + } + return valid; + } + + @Override + public void destroy() { + } + + public SSOAuthenticationProperties loadJwtProperties() { + String providerUrl = configuration.getString(JWT_AUTH_PROVIDER_URL); + if (providerUrl != null && configuration.getBoolean("atlas.sso.knox.enabled", false)) { + SSOAuthenticationProperties jwtProperties = new SSOAuthenticationProperties(); + String publicKeyPathStr = configuration.getString(JWT_PUBLIC_KEY); + if (publicKeyPathStr == null) { + LOG.error("Public key pem not specified for SSO auth provider {}. SSO auth will be disabled"); + return null; + } + jwtProperties.setAuthenticationProviderUrl(providerUrl); + jwtProperties.setCookieName(configuration.getString(JWT_COOKIE_NAME, JWT_COOKIE_NAME_DEFAULT)); + jwtProperties.setOriginalUrlQueryParam(configuration.getString(JWT_ORIGINAL_URL_QUERY_PARAM, JWT_ORIGINAL_URL_QUERY_PARAM_DEFAULT)); + String userAgent = configuration.getString(BROWSER_USERAGENT); + if (userAgent != null && !userAgent.isEmpty()) { + jwtProperties.setUserAgentList(userAgent.split(",")); + } + try { + RSAPublicKey publicKey = parseRSAPublicKey(publicKeyPathStr); + jwtProperties.setPublicKey(publicKey); + } catch (IOException e) { + LOG.error("Unable to read public certificate file. JWT auth will be disabled.", e); + } catch (CertificateException e) { + LOG.error("Unable to parse public certificate file. JWT auth will be disabled.", e); + } catch (ServletException e) { + LOG.error("ServletException while processing the properties", e); + } + return jwtProperties; + } else { + return null; + } + } + + /* + * public static RSAPublicKey getPublicKeyFromFile(String filePath) throws + * IOException, CertificateException { + * FileUtils.readFileToString(new File(filePath)); + * getPublicKeyFromString(pemString); } + */ + + public static RSAPublicKey parseRSAPublicKey(String pem) + throws CertificateException, UnsupportedEncodingException, + ServletException { + String PEM_HEADER = "-----BEGIN CERTIFICATE-----\n"; + String PEM_FOOTER = "\n-----END CERTIFICATE-----"; + String fullPem = PEM_HEADER + pem + PEM_FOOTER; + PublicKey key = null; + try { + CertificateFactory fact = CertificateFactory.getInstance("X.509"); + ByteArrayInputStream is = new ByteArrayInputStream(fullPem.getBytes("UTF8")); + X509Certificate cer = (X509Certificate) fact.generateCertificate(is); + key = cer.getPublicKey(); + } catch (CertificateException ce) { + String message = null; + if (pem.startsWith(PEM_HEADER)) { + message = "CertificateException - be sure not to include PEM header " + "and footer in the PEM configuration element."; + } else { + message = "CertificateException - PEM may be corrupt"; + } + throw new ServletException(message, ce); + } catch (UnsupportedEncodingException uee) { + throw new ServletException(uee); + } + return (RSAPublicKey) key; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthentication.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthentication.java b/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthentication.java new file mode 100644 index 0000000..a96d29f --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthentication.java @@ -0,0 +1,74 @@ +/* + * 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 com.nimbusds.jwt.SignedJWT; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +/** + * Internal token which describes JWT authentication + */ +public class SSOAuthentication implements Authentication { + + private final SignedJWT token; + private boolean authenticated = false; + + public SSOAuthentication(SignedJWT token) { + this.token = token; + } + + @Override + public SignedJWT getCredentials() { + return token; + } + + @Override + public Object getDetails() { + return null; + } + + @Override + public boolean isAuthenticated() { + return authenticated; + } + + @Override + public void setAuthenticated(boolean authenticated) throws IllegalArgumentException { + this.authenticated = authenticated; + } + + @Override + public String getName() { + return null; + } + + @Override + public Collection<? extends GrantedAuthority> getAuthorities() { + return null; + } + + @Override + public Object getPrincipal() { + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthenticationProperties.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthenticationProperties.java b/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthenticationProperties.java new file mode 100644 index 0000000..5d5ed17 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthenticationProperties.java @@ -0,0 +1,78 @@ +/* + * 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.security.interfaces.RSAPublicKey; + +public class SSOAuthenticationProperties { + + private String authenticationProviderUrl = null; + private RSAPublicKey publicKey = null; + private String cookieName = "hadoop-jwt"; + private String originalUrlQueryParam = null; + private String[] userAgentList = null; + + public String getAuthenticationProviderUrl() { + return authenticationProviderUrl; + } + + public void setAuthenticationProviderUrl(String authenticationProviderUrl) { + this.authenticationProviderUrl = authenticationProviderUrl; + } + + public RSAPublicKey getPublicKey() { + return publicKey; + } + + public void setPublicKey(RSAPublicKey publicKey) { + this.publicKey = publicKey; + } + + public String getCookieName() { + return cookieName; + } + + public void setCookieName(String cookieName) { + this.cookieName = cookieName; + } + + public String getOriginalUrlQueryParam() { + return originalUrlQueryParam; + } + + public void setOriginalUrlQueryParam(String originalUrlQueryParam) { + this.originalUrlQueryParam = originalUrlQueryParam; + } + + /** + * @return the userAgentList + */ + public String[] getUserAgentList() { + return userAgentList; + } + + /** + * @param userAgentList the userAgentList to set + */ + public void setUserAgentList(String[] userAgentList) { + this.userAgentList = userAgentList; + } +} + http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/webapp/src/main/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProvider.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProvider.java index 595387a..b99a30a 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProvider.java @@ -22,7 +22,11 @@ package org.apache.atlas.web.security; import java.util.ArrayList; import java.util.List; +import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -33,6 +37,7 @@ import org.springframework.security.core.userdetails.UserDetails; public abstract class AtlasAbstractAuthenticationProvider implements AuthenticationProvider { + private static final Logger LOG = LoggerFactory.getLogger(AtlasAbstractAuthenticationProvider.class); @Override public boolean supports(Class<?> authentication) { @@ -92,16 +97,20 @@ public abstract class AtlasAbstractAuthenticationProvider implements return authentication; } - public List<GrantedAuthority> getAuthoritiesFromUGI(String userName) { + public static List<GrantedAuthority> getAuthoritiesFromUGI(String userName) { List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>(); - UserGroupInformation ugi = UserGroupInformation.createRemoteUser(userName); - if (ugi != null) { - String[] userGroups = ugi.getGroupNames(); + Configuration config = new Configuration(); + + try { + Groups gp = new Groups(config); + List<String> userGroups = gp.getGroups(userName); if (userGroups != null) { for (String group : userGroups) { grantedAuths.add(new SimpleGrantedAuthority(group)); } } + } catch (java.io.IOException e) { + LOG.error("Exception while fetching groups ", e); } return grantedAuths; } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java index 23d3d70..68553df 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java @@ -39,6 +39,10 @@ public class AtlasAuthenticationProvider extends public static final String FILE_AUTH_METHOD = "atlas.authentication.method.file"; public static final String LDAP_TYPE = "atlas.authentication.method.ldap.type"; + + + private boolean ssoEnabled = false; + @Autowired AtlasLdapAuthenticationProvider ldapAuthenticationProvider; @@ -67,17 +71,27 @@ public class AtlasAuthenticationProvider extends public Authentication authenticate(Authentication authentication) throws AuthenticationException { - if (ldapType.equalsIgnoreCase("LDAP")) { - try { - authentication = ldapAuthenticationProvider.authenticate(authentication); - } catch (Exception ex) { - LOG.error("Error while LDAP authentication", ex); + if(ssoEnabled){ + if (authentication != null){ + authentication = getSSOAuthentication(authentication); + if(authentication!=null && authentication.isAuthenticated()){ + return authentication; + } } - } else if (ldapType.equalsIgnoreCase("AD")) { - try { - authentication = adAuthenticationProvider.authenticate(authentication); - } catch (Exception ex) { - LOG.error("Error while AD authentication", ex); + } else { + + if (ldapType.equalsIgnoreCase("LDAP")) { + try { + authentication = ldapAuthenticationProvider.authenticate(authentication); + } catch (Exception ex) { + LOG.error("Error while LDAP authentication", ex); + } + } else if (ldapType.equalsIgnoreCase("AD")) { + try { + authentication = adAuthenticationProvider.authenticate(authentication); + } catch (Exception ex) { + LOG.error("Error while AD authentication", ex); + } } } @@ -97,4 +111,15 @@ public class AtlasAuthenticationProvider extends throw new AtlasAuthenticationException("Authentication failed."); } + public boolean isSsoEnabled() { + return ssoEnabled; + } + + public void setSsoEnabled(boolean ssoEnabled) { + this.ssoEnabled = ssoEnabled; + } + + private Authentication getSSOAuthentication(Authentication authentication) throws AuthenticationException{ + return authentication; + } } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java ---------------------------------------------------------------------- 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 8654716..31a9ec0 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 @@ -43,6 +43,11 @@ public class AtlasAuthenticationSuccessHandler implements AuthenticationSuccessH ObjectMapper mapper = new ObjectMapper(); 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"); + } + String jsonAsStr = mapper.writeValueAsString(json); response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/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 5395d4e..4ba3025 100644 --- a/webapp/src/main/resources/spring-security.xml +++ b/webapp/src/main/resources/spring-security.xml @@ -42,6 +42,7 @@ <security:session-management session-fixation-protection="newSession" /> <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" /> @@ -53,7 +54,7 @@ username-parameter="j_username" password-parameter="j_password" /> - <security:logout logout-success-url="/login.jsp" delete-cookies="JSESSIONID" + <security:logout logout-success-url="/login.jsp" delete-cookies="ATLASSESSIONID" logout-url="/logout.html" /> <http-basic /> <security:custom-filter position="LAST" ref="atlasAuthorizationFilter"/> @@ -61,7 +62,10 @@ <beans:bean id="krbAuthenticationFilter" class="org.apache.atlas.web.filters.AtlasAuthenticationFilter"> </beans:bean> - + + <beans:bean id="ssoAuthenticationFilter" class="org.apache.atlas.web.filters.AtlasKnoxSSOAuthenticationFilter"> + </beans:bean> + <beans:bean id="CSRFPreventionFilter" class="org.apache.atlas.web.filters.AtlasCSRFPreventionFilter"> </beans:bean> http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/webapp/src/main/webapp/WEB-INF/web.xml ---------------------------------------------------------------------- diff --git a/webapp/src/main/webapp/WEB-INF/web.xml b/webapp/src/main/webapp/WEB-INF/web.xml index 7c6bc6d..f7e2028 100755 --- a/webapp/src/main/webapp/WEB-INF/web.xml +++ b/webapp/src/main/webapp/WEB-INF/web.xml @@ -79,5 +79,14 @@ </listener> + <session-config> + <session-timeout>60</session-timeout> + <tracking-mode>COOKIE</tracking-mode> + <cookie-config> + <name>ATLASSESSIONID</name> + <http-only>true</http-only> + </cookie-config> + </session-config> + </web-app> http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/854b7928/webapp/src/test/webapp/WEB-INF/web.xml ---------------------------------------------------------------------- diff --git a/webapp/src/test/webapp/WEB-INF/web.xml b/webapp/src/test/webapp/WEB-INF/web.xml index 1b152ee..6649043 100755 --- a/webapp/src/test/webapp/WEB-INF/web.xml +++ b/webapp/src/test/webapp/WEB-INF/web.xml @@ -73,4 +73,13 @@ <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> + <session-config> + <session-timeout>60</session-timeout> + <tracking-mode>COOKIE</tracking-mode> + <cookie-config> + <name>ATLASSESSIONID</name> + <http-only>true</http-only> + </cookie-config> + </session-config> + </web-app>
