Repository: knox Updated Branches: refs/heads/master 9c7aa7e1c -> bb467b8c4
KNOX-1082 - Add support to validate the "nbf" claim for JWTs Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/bb467b8c Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/bb467b8c Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/bb467b8c Branch: refs/heads/master Commit: bb467b8c4ecd87fc83ec1cf2863767b0330f171e Parents: 9c7aa7e Author: Colm O hEigeartaigh <cohei...@apache.org> Authored: Tue Oct 17 12:49:04 2017 +0100 Committer: Colm O hEigeartaigh <cohei...@apache.org> Committed: Tue Oct 17 12:49:04 2017 +0100 ---------------------------------------------------------------------- .../provider/federation/jwt/JWTMessages.java | 3 ++ .../jwt/filter/AbstractJWTFilter.java | 9 ++++- .../federation/AbstractJWTFilterTest.java | 40 ++++++++++++++++++-- .../services/security/token/impl/JWT.java | 3 ++ .../services/security/token/impl/JWTToken.java | 11 ++++++ 5 files changed, 61 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/bb467b8c/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 f6969c6..f38d13b 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 @@ -34,6 +34,9 @@ public interface JWTMessages { @Message( level = MessageLevel.INFO, text = "Access token has expired; a new one must be acquired." ) void tokenHasExpired(); + @Message( level = MessageLevel.INFO, text = "The NotBefore check failed." ) + void notBeforeCheckFailed(); + @Message( level = MessageLevel.WARN, text = "Expected Bearer token is missing." ) void missingBearerToken(); http://git-wip-us.apache.org/repos/asf/knox/blob/bb467b8c/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java index deb3d5b..0d8ecb8 100644 --- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java +++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java @@ -275,7 +275,14 @@ public abstract class AbstractJWTFilter implements Filter { if (tokenIsStillValid(token)) { boolean audValid = validateAudiences(token); if (audValid) { - return true; + Date nbf = token.getNotBeforeDate(); + if (nbf == null || new Date().after(nbf)) { + return true; + } else { + log.notBeforeCheckFailed(); + handleValidationError(request, response, HttpServletResponse.SC_BAD_REQUEST, + "Bad request: the NotBefore check failed"); + } } else { log.failedToValidateAudience(); http://git-wip-us.apache.org/repos/asf/knox/blob/bb467b8c/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java ---------------------------------------------------------------------- diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java index b261081..54c596b 100644 --- a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java +++ b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java @@ -505,7 +505,7 @@ public abstract class AbstractJWTFilterTest { handler.init(new TestFilterConfig(props)); SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", new Date(new Date().getTime() + 5000), - privateKey, JWSAlgorithm.RS512.getName()); + new Date(), privateKey, JWSAlgorithm.RS512.getName()); HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); setTokenOnRequest(request, jwt); @@ -536,7 +536,7 @@ public abstract class AbstractJWTFilterTest { handler.init(new TestFilterConfig(props)); SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", new Date(new Date().getTime() + 5000), - privateKey, JWSAlgorithm.RS384.getName()); + new Date(), privateKey, JWSAlgorithm.RS384.getName()); HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); setTokenOnRequest(request, jwt); @@ -558,6 +558,37 @@ public abstract class AbstractJWTFilterTest { } } + @Test + public void testNotBeforeJWT() throws Exception { + try { + Properties props = getProperties(); + handler.init(new TestFilterConfig(props)); + + SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", + new Date(new Date().getTime() + 5000), + new Date(new Date().getTime() + 5000), privateKey, + JWSAlgorithm.RS256.getName()); + + HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); + setTokenOnRequest(request, jwt); + + EasyMock.expect(request.getRequestURL()).andReturn( + new StringBuffer(SERVICE_URL)).anyTimes(); + EasyMock.expect(request.getQueryString()).andReturn(null); + HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); + EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn( + SERVICE_URL); + EasyMock.replay(request); + + TestFilterChain chain = new TestFilterChain(); + handler.doFilter(request, response, chain); + Assert.assertTrue("doFilterCalled should not be false.", !chain.doFilterCalled); + Assert.assertTrue("No Subject should be returned.", chain.subject == null); + } catch (ServletException se) { + fail("Should NOT have thrown a ServletException."); + } + } + protected Properties getProperties() { Properties props = new Properties(); props.setProperty( @@ -568,10 +599,10 @@ public abstract class AbstractJWTFilterTest { protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey) throws Exception { - return getJWT(issuer, sub, expires, privateKey, JWSAlgorithm.RS256.getName()); + return getJWT(issuer, sub, expires, new Date(), privateKey, JWSAlgorithm.RS256.getName()); } - protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey, + protected SignedJWT getJWT(String issuer, String sub, Date expires, Date nbf, RSAPrivateKey privateKey, String signatureAlgorithm) throws Exception { List<String> aud = new ArrayList<String>(); @@ -582,6 +613,7 @@ public abstract class AbstractJWTFilterTest { .subject(sub) .audience(aud) .expirationTime(expires) + .notBeforeTime(nbf) .claim("scope", "openid") .build(); http://git-wip-us.apache.org/repos/asf/knox/blob/bb467b8c/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 1a6f4f9..fa9076e 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 @@ -29,6 +29,7 @@ public interface JWT { String ISSUER = "iss"; String AUDIENCE = "aud"; String EXPIRES = "exp"; + String NOT_BEFORE = "nbf"; String getPayload(); @@ -50,6 +51,8 @@ public interface JWT { Date getExpiresDate(); + Date getNotBeforeDate(); + String getSubject(); String getHeader(); http://git-wip-us.apache.org/repos/asf/knox/blob/bb467b8c/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 be2a331..f985caf 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 @@ -231,6 +231,17 @@ public class JWTToken implements JWT { return date; } + @Override + public Date getNotBeforeDate() { + Date date = null; + try { + date = jwt.getJWTClaimsSet().getNotBeforeTime(); + } catch (ParseException e) { + log.unableToParseToken(e); + } + return date; + } + /* (non-Javadoc) * @see org.apache.hadoop.gateway.services.security.token.impl.JWT#getPrincipal() */