You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by lm...@apache.org on 2015/09/30 15:58:42 UTC
knox git commit: KNOX-602 JWT/SSO Cookie Based Federation Provider
Repository: knox
Updated Branches:
refs/heads/master 48b4ad74d -> 87fcd4d65
KNOX-602 JWT/SSO Cookie Based Federation Provider
Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/87fcd4d6
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/87fcd4d6
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/87fcd4d6
Branch: refs/heads/master
Commit: 87fcd4d65bf6ec1bc97e346cd6003d41399b272f
Parents: 48b4ad7
Author: Larry McCay <lm...@hortonworks.com>
Authored: Wed Sep 30 09:58:23 2015 -0400
Committer: Larry McCay <lm...@hortonworks.com>
Committed: Wed Sep 30 09:58:23 2015 -0400
----------------------------------------------------------------------
CHANGES | 1 +
gateway-provider-security-jwt/pom.xml | 5 +
.../provider/federation/jwt/JWTMessages.java | 19 +-
.../deploy/SSOCookieFederationContributor.java | 64 +++++
.../jwt/filter/SSOCookieFederationFilter.java | 283 +++++++++++++++++++
...gateway.deploy.ProviderDeploymentContributor | 1 +
.../impl/DefaultTokenAuthorityService.java | 8 +
.../service/knoxsso/KnoxSSOMessages.java | 6 +
.../gateway/service/knoxsso/WebSSOResource.java | 25 +-
.../security/token/JWTokenAuthority.java | 3 +
.../services/security/token/impl/JWT.java | 6 +
.../services/security/token/impl/JWTToken.java | 53 +++-
pom.xml | 6 +
13 files changed, 463 insertions(+), 17 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index cb802ad..51925ad 100644
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,7 @@ Release Notes - Apache Knox - Version 0.7.0
* [KNOX-548] - KnoxCLI adds a new system-user-auth-test command to test a topology's system username and password
* [KNOX-549] - New Service-Test API can be added to topology. Accessible via Http call or KnoxCLI
* [KNOX-579] - Regex based identity assertion provider with static dictionary lookup
+ * [KNOX-602] - JWT/SSO Cookie Based Federation Provider
** Improvement
* [KNOX-553] - Added topology validation from KnoxCLI to TopologyService deployment.
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-provider-security-jwt/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/pom.xml b/gateway-provider-security-jwt/pom.xml
index 1700c99..40077d7 100644
--- a/gateway-provider-security-jwt/pom.xml
+++ b/gateway-provider-security-jwt/pom.xml
@@ -52,6 +52,11 @@
</dependency>
<dependency>
+ <groupId>com.thetransactioncompany</groupId>
+ <artifactId>cors-filter</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/JWTMessages.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/JWTMessages.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/JWTMessages.java
index 08561ff..f6969c6 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/JWTMessages.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/JWTMessages.java
@@ -1,4 +1,5 @@
/**
+
* 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
@@ -24,16 +25,16 @@ import org.apache.hadoop.gateway.i18n.messages.StackTrace;
@Messages(logger="org.apache.hadoop.gateway.provider.federation.jwt")
public interface JWTMessages {
- @Message( level = MessageLevel.INFO, text = "Failed to validate the audience attribute." )
+ @Message( level = MessageLevel.WARN, text = "Failed to validate the audience attribute." )
void failedToValidateAudience();
- @Message( level = MessageLevel.INFO, text = "Failed to verify the token signature." )
+ @Message( level = MessageLevel.WARN, text = "Failed to verify the token signature." )
void failedToVerifyTokenSignature();
@Message( level = MessageLevel.INFO, text = "Access token has expired; a new one must be acquired." )
void tokenHasExpired();
- @Message( level = MessageLevel.INFO, text = "Expected Bearer token is missing." )
+ @Message( level = MessageLevel.WARN, text = "Expected Bearer token is missing." )
void missingBearerToken();
@Message( level = MessageLevel.INFO, text = "Unable to verify token: {0}" )
@@ -41,4 +42,16 @@ public interface JWTMessages {
@Message( level = MessageLevel.ERROR, text = "Unable to verify token: {0}" )
void unableToIssueToken(@StackTrace( level = MessageLevel.DEBUG) Exception e);
+
+ @Message( level = MessageLevel.DEBUG, text = "Sending redirect to: {0}" )
+ void sendRedirectToLoginURL(String loginURL);
+
+ @Message( level = MessageLevel.ERROR, text = "Required configuration element for authentication provider is missing." )
+ void missingAuthenticationProviderUrlConfiguration();
+
+ @Message( level = MessageLevel.DEBUG, text = "{0} Cookie has been found and is being processed." )
+ void cookieHasBeenFound(String cookieName);
+
+ @Message( level = MessageLevel.DEBUG, text = "Audience claim has been validated." )
+ void jwtAudienceValidated();
}
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/SSOCookieFederationContributor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/SSOCookieFederationContributor.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/SSOCookieFederationContributor.java
new file mode 100644
index 0000000..12b35e1
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/deploy/SSOCookieFederationContributor.java
@@ -0,0 +1,64 @@
+/**
+ * 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.hadoop.gateway.provider.federation.jwt.deploy;
+
+import org.apache.hadoop.gateway.deploy.DeploymentContext;
+import org.apache.hadoop.gateway.deploy.ProviderDeploymentContributorBase;
+import org.apache.hadoop.gateway.descriptor.FilterParamDescriptor;
+import org.apache.hadoop.gateway.descriptor.ResourceDescriptor;
+import org.apache.hadoop.gateway.topology.Provider;
+import org.apache.hadoop.gateway.topology.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class SSOCookieFederationContributor extends ProviderDeploymentContributorBase {
+
+ private static final String FILTER_CLASSNAME = "org.apache.hadoop.gateway.provider.federation.jwt.filter.SSOCookieFederationFilter";
+ private static final String CORS_FILTER_CLASSNAME = "com.thetransactioncompany.cors.CORSFilter";
+
+ @Override
+ public String getRole() {
+ return "federation";
+ }
+
+ @Override
+ public String getName() {
+ return "SSOCookieProvider";
+ }
+
+ @Override
+ public void contributeProvider( DeploymentContext context, Provider provider ) {
+ }
+
+ @Override
+ public void contributeFilter( DeploymentContext context, Provider provider, Service service, ResourceDescriptor resource, List<FilterParamDescriptor> params ) {
+ // blindly add all the provider params as filter init params
+ if (params == null) {
+ params = new ArrayList<FilterParamDescriptor>();
+ }
+ Map<String, String> providerParams = provider.getParams();
+ for(Entry<String, String> entry : providerParams.entrySet()) {
+ params.add( resource.createFilterParam().name( entry.getKey().toLowerCase() ).value( entry.getValue() ) );
+ }
+ resource.addFilter().name( "CORS" ).role( getRole() ).impl( CORS_FILTER_CLASSNAME ).params( params );
+ resource.addFilter().name( getName() ).role( getRole() ).impl( FILTER_CLASSNAME ).params( params );
+ }
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
new file mode 100644
index 0000000..18a9eea
--- /dev/null
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
@@ -0,0 +1,283 @@
+/**
+ * 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.hadoop.gateway.provider.federation.jwt.filter;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+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.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
+import org.apache.hadoop.gateway.provider.federation.jwt.JWTMessages;
+import org.apache.hadoop.gateway.security.PrimaryPrincipal;
+import org.apache.hadoop.gateway.services.GatewayServices;
+import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
+import org.apache.hadoop.gateway.services.security.token.TokenServiceException;
+import org.apache.hadoop.gateway.services.security.token.impl.JWTToken;
+
+public class SSOCookieFederationFilter implements Filter {
+ private static JWTMessages log = MessagesFactory.get( JWTMessages.class );
+ private static final String ORIGINAL_URL_QUERY_PARAM = "originalUrl=";
+ private static final String SSO_COOKIE_NAME = "sso.cookie.name";
+ private static final String SSO_EXPECTED_AUDIENCES = "sso.expected.audiences";
+ private static final String SSO_AUTHENTICATION_PROVIDER_URL = "sso.authentication.provider.url";
+ private static final String DEFAULT_SSO_COOKIE_NAME = "hadoop-jwt";
+
+ private JWTokenAuthority authority = null;
+ private String cookieName = null;
+ private List<String> audiences = null;
+ private String authenticationProviderUrl = null;
+
+ @Override
+ public void init( FilterConfig filterConfig ) throws ServletException {
+ GatewayServices services = (GatewayServices) filterConfig.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
+ authority = (JWTokenAuthority) services.getService(GatewayServices.TOKEN_SERVICE);
+
+ // configured cookieName
+ cookieName = filterConfig.getInitParameter(SSO_COOKIE_NAME);
+ if (cookieName == null) {
+ cookieName = DEFAULT_SSO_COOKIE_NAME;
+ }
+
+ // expected audiences or null
+ String expectedAudiences = filterConfig.getInitParameter(SSO_EXPECTED_AUDIENCES);
+ if (expectedAudiences != null) {
+ audiences = parseExpectedAudiences(expectedAudiences);
+ }
+
+ // url to SSO authentication provider
+ authenticationProviderUrl = filterConfig.getInitParameter(SSO_AUTHENTICATION_PROVIDER_URL);
+ if (authenticationProviderUrl == null) {
+ log.missingAuthenticationProviderUrlConfiguration();
+ }
+ }
+
+ /**
+ * @param expectedAudiences
+ * @return
+ */
+ private List<String> parseExpectedAudiences(String expectedAudiences) {
+ ArrayList<String> audList = null;
+ // setup the list of valid audiences for token validation
+ if (expectedAudiences != null) {
+ // parse into the list
+ String[] audArray = expectedAudiences.split(",");
+ audList = new ArrayList<String>();
+ for (String a : audArray) {
+ audList.add(a);
+ }
+ }
+ return audList;
+ }
+
+ public void destroy() {
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ String wireToken = null;
+ HttpServletRequest req = (HttpServletRequest) request;
+
+ String loginURL = constructLoginURL(req);
+ wireToken = getJWTFromCookie(req);
+ if (wireToken == null) {
+ if (req.getMethod().equals("OPTIONS")) {
+ // CORS preflight requests to determine allowed origins and related config
+ // must be able to continue without being redirected
+ Subject sub = new Subject();
+ sub.getPrincipals().add(new PrimaryPrincipal("anonymous"));
+ continueWithEstablishedSecurityContext(sub, req, (HttpServletResponse) response, chain);
+ }
+ log.sendRedirectToLoginURL(loginURL);
+ ((HttpServletResponse) response).sendRedirect(loginURL);
+ }
+ else {
+ JWTToken token = new JWTToken(wireToken);
+ boolean verified = false;
+ try {
+ verified = authority.verifyToken(token);
+ if (verified) {
+ Date expires = token.getExpiresDate();
+ if (expires != null && new Date().before(expires)) {
+ boolean audValid = validateAudiences(token);
+ if (audValid) {
+ Subject subject = createSubjectFromToken(token);
+ continueWithEstablishedSecurityContext(subject, (HttpServletRequest)request, (HttpServletResponse)response, chain);
+ }
+ else {
+ log.failedToValidateAudience();
+ ((HttpServletResponse) response).sendRedirect(loginURL);
+ }
+ }
+ else {
+ log.tokenHasExpired();
+ ((HttpServletResponse) response).sendRedirect(loginURL);
+ }
+ }
+ else {
+ log.failedToVerifyTokenSignature();
+ ((HttpServletResponse) response).sendRedirect(loginURL);
+ }
+ } catch (TokenServiceException e) {
+ log.unableToVerifyToken(e);
+ ((HttpServletResponse) response).sendRedirect(loginURL);
+ }
+ }
+ }
+
+ /**
+ * 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 (cookies != null) {
+ for (Cookie cookie : cookies) {
+ if (cookieName.equals(cookie.getName())) {
+ log.cookieHasBeenFound(cookieName);
+ 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 = "&";
+ }
+ String loginURL = authenticationProviderUrl + delimiter
+ + ORIGINAL_URL_QUERY_PARAM
+ + request.getRequestURL().toString() + "?" + request.getQueryString();
+ return loginURL;
+ }
+
+ /**
+ * Validate whether any of the accepted audience claims is present in the
+ * issued token claims list for audience. Override this method in subclasses
+ * in order to customize the audience validation behavior.
+ *
+ * @param jwtToken
+ * the JWT token where the allowed audiences will be found
+ * @return true if an expected audience is present, otherwise false
+ */
+ protected boolean validateAudiences(JWTToken jwtToken) {
+ boolean valid = false;
+
+ String[] tokenAudienceList = jwtToken.getAudienceClaims();
+ // if there were no expected audiences configured then just
+ // consider any audience acceptable
+ if (audiences == null) {
+ valid = true;
+ } else {
+ // if any of the configured audiences is found then consider it
+ // acceptable
+ for (String aud : tokenAudienceList) {
+ if (audiences.contains(aud)) {
+ //log.debug("JWT token audience has been successfully validated");
+ log.jwtAudienceValidated();
+ valid = true;
+ break;
+ }
+ }
+ }
+ return valid;
+ }
+
+ private void sendUnauthorized(ServletResponse response) throws IOException {
+ ((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+ }
+
+ private void continueWithEstablishedSecurityContext(Subject subject, final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException {
+ try {
+ Subject.doAs(
+ subject,
+ new PrivilegedExceptionAction<Object>() {
+ @Override
+ public Object run() throws Exception {
+ chain.doFilter(request, response);
+ return null;
+ }
+ }
+ );
+ }
+ catch (PrivilegedActionException e) {
+ Throwable t = e.getCause();
+ if (t instanceof IOException) {
+ throw (IOException) t;
+ }
+ else if (t instanceof ServletException) {
+ throw (ServletException) t;
+ }
+ else {
+ throw new ServletException(t);
+ }
+ }
+ }
+
+ private Subject createSubjectFromToken(JWTToken token) {
+ final String principal = token.getSubject();
+
+ @SuppressWarnings("rawtypes")
+ HashSet emptySet = new HashSet();
+ Set<Principal> principals = new HashSet<Principal>();
+ Principal p = new PrimaryPrincipal(principal);
+ principals.add(p);
+
+// The newly constructed Sets check whether this Subject has been set read-only
+// before permitting subsequent modifications. The newly created Sets also prevent
+// illegal modifications by ensuring that callers have sufficient permissions.
+//
+// To modify the Principals Set, the caller must have AuthPermission("modifyPrincipals").
+// To modify the public credential Set, the caller must have AuthPermission("modifyPublicCredentials").
+// To modify the private credential Set, the caller must have AuthPermission("modifyPrivateCredentials").
+ javax.security.auth.Subject subject = new javax.security.auth.Subject(true, principals, emptySet, emptySet);
+ return subject;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor b/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
index 2300504..cd69d46 100644
--- a/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
+++ b/gateway-provider-security-jwt/src/main/resources/META-INF/services/org.apache.hadoop.gateway.deploy.ProviderDeploymentContributor
@@ -20,3 +20,4 @@ org.apache.hadoop.gateway.provider.federation.jwt.deploy.JWTFederationContributo
org.apache.hadoop.gateway.provider.federation.jwt.deploy.JWTAccessTokenAssertionContributor
org.apache.hadoop.gateway.provider.federation.jwt.deploy.JWTAuthCodeAssertionContributor
org.apache.hadoop.gateway.provider.federation.jwt.deploy.AccessTokenFederationContributor
+org.apache.hadoop.gateway.provider.federation.jwt.deploy.SSOCookieFederationContributor
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
index d4e5c5f..d28efa7 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
@@ -72,6 +72,14 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
return issueToken(p, null, algorithm);
}
+ /* (non-Javadoc)
+ * @see org.apache.hadoop.gateway.provider.federation.jwt.JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, long expires)
+ */
+ @Override
+ public JWTToken issueToken(Principal p, String algorithm, long expires) throws TokenServiceException {
+ return issueToken(p, null, algorithm, expires);
+ }
+
public JWTToken issueToken(Principal p, String audience, String algorithm)
throws TokenServiceException {
return issueToken(p, audience, algorithm, -1);
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/KnoxSSOMessages.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/KnoxSSOMessages.java b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/KnoxSSOMessages.java
index 2c0b933..598fb99 100644
--- a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/KnoxSSOMessages.java
+++ b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/KnoxSSOMessages.java
@@ -53,4 +53,10 @@ public interface KnoxSSOMessages {
@Message( level = MessageLevel.WARN, text = "The SSO cookie max age configuration is invalid: {0} - using default.")
void invalidMaxAgeEncountered(String age);
+
+ @Message( level = MessageLevel.WARN, text = "The SSO token time to live - ttl is invalid: {0} - using default.")
+ void invalidTokenTTLEncountered(String ttl);
+
+ @Message( level = MessageLevel.INFO, text = "The cookie max age is being set to: {0}.")
+ void setMaxAge(String age);
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
index 056fdf2..644d650 100644
--- a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
+++ b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
@@ -48,13 +48,15 @@ import static javax.ws.rs.core.MediaType.APPLICATION_XML;
public class WebSSOResource {
private static final String SSO_COOKIE_SECURE_ONLY_INIT_PARAM = "knoxsso.cookie.secure.only";
private static final String SSO_COOKIE_MAX_AGE_INIT_PARAM = "knoxsso.cookie.max.age";
+ private static final String SSO_COOKIE_TOKEN_TTL_PARAM = "knoxsso.token.ttl";
private static final String ORIGINAL_URL_REQUEST_PARAM = "originalUrl";
private static final String ORIGINAL_URL_COOKIE_NAME = "original-url";
private static final String JWT_COOKIE_NAME = "hadoop-jwt";
static final String RESOURCE_PATH = "/knoxsso/api/v1/websso";
private static KnoxSSOMessages log = MessagesFactory.get( KnoxSSOMessages.class );
private boolean secureOnly = true;
- private int maxAge = 120;
+ private int maxAge = -1;
+ private long tokenTTL = 30000l;
@Context
private HttpServletRequest request;
@@ -70,18 +72,31 @@ public class WebSSOResource {
String secure = context.getInitParameter(SSO_COOKIE_SECURE_ONLY_INIT_PARAM);
if (secure != null) {
secureOnly = ("false".equals(secure) ? false : true);
- log.cookieSecureOnly(secureOnly);
+ if (!secureOnly) {
+ log.cookieSecureOnly(secureOnly);
+ }
}
String age = context.getInitParameter(SSO_COOKIE_MAX_AGE_INIT_PARAM);
if (age != null) {
try {
+ log.setMaxAge(age);
maxAge = Integer.parseInt(age);
}
catch (NumberFormatException nfe) {
log.invalidMaxAgeEncountered(age);
}
}
+
+ String ttl = context.getInitParameter(SSO_COOKIE_TOKEN_TTL_PARAM);
+ if (ttl != null) {
+ try {
+ tokenTTL = Long.parseLong(ttl);
+ }
+ catch (NumberFormatException nfe) {
+ log.invalidTokenTTLEncountered(ttl);
+ }
+ }
}
@GET
@@ -116,7 +131,7 @@ public class WebSSOResource {
Principal p = ((HttpServletRequest)request).getUserPrincipal();
try {
- JWT token = ts.issueToken(p, "RS256");
+ JWT token = ts.issueToken(p, "RS256", System.currentTimeMillis() + tokenTTL);
addJWTHadoopCookie(original, token);
@@ -150,7 +165,9 @@ public class WebSSOResource {
if (secureOnly) {
c.setSecure(true);
}
- c.setMaxAge(maxAge);
+ if (maxAge != -1) {
+ c.setMaxAge(maxAge);
+ }
response.addCookie(c);
log.addedJWTCookie();
}
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/JWTokenAuthority.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/JWTokenAuthority.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/JWTokenAuthority.java
index bb978bf..7ed3ab5 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/JWTokenAuthority.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/JWTokenAuthority.java
@@ -21,6 +21,7 @@ import java.security.Principal;
import javax.security.auth.Subject;
+import org.apache.hadoop.gateway.services.security.token.impl.JWT;
import org.apache.hadoop.gateway.services.security.token.impl.JWTToken;
public interface JWTokenAuthority {
@@ -38,4 +39,6 @@ public interface JWTokenAuthority {
JWTToken issueToken(Principal p, String audience, String algorithm,
long expires) throws TokenServiceException;
+
+ JWT issueToken(Principal p, String audience, long l) throws TokenServiceException;
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWT.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWT.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWT.java
index ca9e912..4321c0d 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWT.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWT.java
@@ -17,6 +17,8 @@
*/
package org.apache.hadoop.gateway.services.security.token.impl;
+import java.util.Date;
+
import com.nimbusds.jose.JWSSigner;
/**
@@ -45,8 +47,12 @@ public interface JWT {
public abstract String getAudience();
+ public String[] getAudienceClaims();
+
public abstract String getExpires();
+ public abstract Date getExpiresDate();
+
public abstract String getSubject();
public abstract String getHeader();
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
index b6c8a1b..71d5020 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
@@ -49,6 +49,14 @@ public class JWTToken implements JWT {
}
}
+ public JWTToken(String serializedJWT) {
+ try {
+ jwt = SignedJWT.parse(serializedJWT);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ }
+
public JWTToken(String alg, String[] claimsArray) {
JWSHeader header = new JWSHeader(new JWSAlgorithm(alg));
JWTClaimsSet claims = new JWTClaimsSet();
@@ -96,7 +104,7 @@ public class JWTToken implements JWT {
public String toString() {
return jwt.serialize();
}
-
+
/* (non-Javadoc)
* @see org.apache.hadoop.gateway.services.security.token.impl.JWT#setSignaturePayload(byte[])
*/
@@ -104,7 +112,7 @@ public class JWTToken implements JWT {
public void setSignaturePayload(byte[] payload) {
// this.payload = payload;
}
-
+
/* (non-Javadoc)
* @see org.apache.hadoop.gateway.services.security.token.impl.JWT#getSignaturePayload()
*/
@@ -128,7 +136,7 @@ public class JWTToken implements JWT {
return jwt;
}
-
+
/* (non-Javadoc)
* @see org.apache.hadoop.gateway.services.security.token.impl.JWT#getClaim(java.lang.String)
*/
@@ -169,18 +177,30 @@ public class JWTToken implements JWT {
public String getAudience() {
String[] claim = null;
String c = null;
-
+
+ claim = getAudienceClaims();
+ if (claim != null) {
+ c = claim[0];
+ }
+
+ return c;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.hadoop.gateway.services.security.token.impl.JWT#getAudienceClaims()
+ */
+ @Override
+ public String[] getAudienceClaims() {
+ String[] claims = null;
+
try {
- claim = jwt.getJWTClaimsSet().getStringArrayClaim(JWT.AUDIENCE);
- if (claim != null) {
- c = claim[0];
- }
+ claims = jwt.getJWTClaimsSet().getStringArrayClaim(JWT.AUDIENCE);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
-
- return c;
+
+ return claims;
}
/* (non-Javadoc)
@@ -191,6 +211,18 @@ public class JWTToken implements JWT {
return getClaim(JWT.EXPIRES);
}
+ @Override
+ public Date getExpiresDate() {
+ Date date = null;
+ try {
+ date = jwt.getJWTClaimsSet().getExpirationTime();
+ } catch (ParseException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return date;
+ }
+
/* (non-Javadoc)
* @see org.apache.hadoop.gateway.services.security.token.impl.JWT#getPrincipal()
*/
@@ -198,6 +230,7 @@ public class JWTToken implements JWT {
public String getPrincipal() {
return getClaim(JWT.PRINCIPAL);
}
+
/* (non-Javadoc)
* @see org.apache.hadoop.gateway.services.security.token.impl.JWT#getPrincipal()
http://git-wip-us.apache.org/repos/asf/knox/blob/87fcd4d6/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index f03fd26..0921d65 100644
--- a/pom.xml
+++ b/pom.xml
@@ -655,6 +655,12 @@
<version>2.5.2</version>
</dependency>
+ <dependency>
+ <groupId>com.thetransactioncompany</groupId>
+ <artifactId>cors-filter</artifactId>
+ <version>2.4</version>
+ </dependency>
+
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>