You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by ma...@apache.org on 2016/11/16 01:47:12 UTC
incubator-atlas git commit: ATLAS-1244: added support for KnoxSSO
Authentication
Repository: incubator-atlas
Updated Branches:
refs/heads/master ae92406d9 -> 854b79280
ATLAS-1244: added support for KnoxSSO Authentication
Signed-off-by: Madhan Neethiraj <ma...@apache.org>
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 <ni...@freestoneinfotech.com>
Authored: Wed Nov 2 16:47:59 2016 +0530
Committer: Madhan Neethiraj <ma...@apache.org>
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>