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 <lmc...@hortonworks.com> Authored: Wed Sep 30 09:58:23 2015 -0400 Committer: Larry McCay <lmc...@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>