cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cohei...@apache.org
Subject [1/2] cxf git commit: Add support for role extraction with JWT validation
Date Mon, 16 Nov 2015 12:01:45 GMT
Repository: cxf
Updated Branches:
  refs/heads/master 591a3ac7f -> 0a7902976


Add support for role extraction with JWT validation


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/0a790297
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/0a790297
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/0a790297

Branch: refs/heads/master
Commit: 0a79029763d4ce920903f833b14304eed3e60138
Parents: 7b76296
Author: Colm O hEigeartaigh <coheigea@apache.org>
Authored: Mon Nov 16 11:34:52 2015 +0000
Committer: Colm O hEigeartaigh <coheigea@apache.org>
Committed: Mon Nov 16 11:35:08 2015 +0000

----------------------------------------------------------------------
 .../validator/jwt/DefaultJWTRoleParser.java     |  89 +++++++++
 .../sts/token/validator/jwt/JWTRoleParser.java  |  44 +++++
 .../token/validator/jwt/JWTTokenValidator.java  |  25 ++-
 .../token/validator/JWTTokenValidatorTest.java  | 198 +++++++++++++++++++
 4 files changed, 349 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/0a790297/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/DefaultJWTRoleParser.java
----------------------------------------------------------------------
diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/DefaultJWTRoleParser.java
b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/DefaultJWTRoleParser.java
new file mode 100644
index 0000000..64d7cf1
--- /dev/null
+++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/DefaultJWTRoleParser.java
@@ -0,0 +1,89 @@
+/**
+ * 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.cxf.sts.token.validator.jwt;
+
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+
+import org.apache.cxf.common.security.SimpleGroup;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.sts.token.validator.DefaultSubjectRoleParser;
+
+/**
+ * A default implementation to extract roles from a JWT token
+ */
+public class DefaultJWTRoleParser extends DefaultSubjectRoleParser implements JWTRoleParser
{
+    
+    private boolean useJaasSubject = true;
+    private String roleClaim;
+
+    /**
+     * Return the set of User/Principal roles from the token.
+     * @param principal the Principal associated with the token
+     * @param subject the JAAS Subject associated with a successful validation of the token
+     * @param token The JWTToken
+     * @return the set of User/Principal roles from the token.
+     */
+    public Set<Principal> parseRolesFromToken(
+        Principal principal, Subject subject, JwtToken token
+    ) {
+        if (subject != null && useJaasSubject) {
+            return super.parseRolesFromSubject(principal, subject);
+        }
+        
+        Set<Principal> roles = null;
+        if (roleClaim != null && token != null && token.getClaims().containsProperty(roleClaim))
{
+            roles = new HashSet<>();
+            String role = token.getClaims().getStringProperty(roleClaim).trim();
+            for (String r : role.split(",")) {
+                roles.add(new SimpleGroup(r));
+            }
+        } else {
+            roles = Collections.emptySet();
+        }
+        
+        return roles;
+    }
+    
+    public boolean isUseJaasSubject() {
+        return useJaasSubject;
+    }
+
+    /**
+     * Whether to get roles from the JAAS Subject (if not null) returned from SAML Assertion
+     * Validation or not. The default is true.
+     * @param useJaasSubject whether to get roles from the JAAS Subject or not
+     */
+    public void setUseJaasSubject(boolean useJaasSubject) {
+        this.useJaasSubject = useJaasSubject;
+    }
+
+    public String getRoleClaim() {
+        return roleClaim;
+    }
+
+    public void setRoleClaim(String roleClaim) {
+        this.roleClaim = roleClaim;
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf/blob/0a790297/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTRoleParser.java
----------------------------------------------------------------------
diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTRoleParser.java
b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTRoleParser.java
new file mode 100644
index 0000000..c651e2a
--- /dev/null
+++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTRoleParser.java
@@ -0,0 +1,44 @@
+/**
+ * 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.cxf.sts.token.validator.jwt;
+
+import java.security.Principal;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.sts.token.validator.SubjectRoleParser;
+
+/**
+ * This interface defines a way to extract roles from a JWT Token
+ */
+public interface JWTRoleParser extends SubjectRoleParser {
+    
+    /**
+     * Return the set of User/Principal roles from the token.
+     * @param principal the Principal associated with the token
+     * @param subject the JAAS Subject associated with a successful validation of the token
+     * @param token The JWTToken
+     * @return the set of User/Principal roles from the token.
+     */
+    Set<Principal> parseRolesFromToken(
+        Principal principal, Subject subject, JwtToken token
+    );
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/0a790297/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java
----------------------------------------------------------------------
diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java
b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java
index 837c3c1..110a611 100644
--- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java
+++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java
@@ -21,6 +21,7 @@ package org.apache.cxf.sts.token.validator.jwt;
 import java.security.KeyStore;
 import java.security.Principal;
 import java.util.Properties;
+import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -51,6 +52,7 @@ public class JWTTokenValidator implements TokenValidator {
     private static final Logger LOG = LogUtils.getL7dLogger(JWTTokenValidator.class);
     private int clockOffset;
     private int ttl;
+    private JWTRoleParser roleParser;
     
     /**
      * Return true if this TokenValidator implementation is capable of validating the
@@ -134,14 +136,8 @@ public class JWTTokenValidator implements TokenValidator {
             return response;
         }
         
+        
         /*
-        // Parse roles from the validated token
-        if (samlRoleParser != null) {
-            Set<Principal> roles = 
-                samlRoleParser.parseRolesFromAssertion(principal, null, assertion);
-            response.setRoles(roles);
-        }
-
         // Get the realm of the SAML token
         String tokenRealm = null;
         if (samlRealmCodec != null) {
@@ -163,6 +159,13 @@ public class JWTTokenValidator implements TokenValidator {
         if (isVerifiedWithAPublicKey(jwt)) {
             Principal principal = new SimplePrincipal(jwt.getClaims().getSubject());
             response.setPrincipal(principal);
+            
+            // Parse roles from the validated token
+            if (roleParser != null) {
+                Set<Principal> roles = 
+                    roleParser.parseRolesFromToken(principal, null, jwt);
+                response.setRoles(roles);
+            }
         }
 
         validateTarget.setState(STATE.VALID);
@@ -204,4 +207,12 @@ public class JWTTokenValidator implements TokenValidator {
     public void setTtl(int ttl) {
         this.ttl = ttl;
     }
+    
+    public JWTRoleParser getRoleParser() {
+        return roleParser;
+    }
+
+    public void setRoleParser(JWTRoleParser roleParser) {
+        this.roleParser = roleParser;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/0a790297/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java
----------------------------------------------------------------------
diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java
b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java
index 3e2bc05..6882751 100644
--- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java
+++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java
@@ -21,6 +21,7 @@ package org.apache.cxf.sts.token.validator;
 import java.io.IOException;
 import java.security.Principal;
 import java.util.Properties;
+import java.util.Set;
 
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
@@ -29,6 +30,7 @@ import javax.security.auth.callback.UnsupportedCallbackException;
 import org.apache.cxf.jaxws.context.WebServiceContextImpl;
 import org.apache.cxf.jaxws.context.WrappedMessageContext;
 import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
 import org.apache.cxf.sts.STSConstants;
 import org.apache.cxf.sts.StaticSTSProperties;
 import org.apache.cxf.sts.cache.DefaultInMemoryTokenStore;
@@ -41,7 +43,11 @@ import org.apache.cxf.sts.service.EncryptionProperties;
 import org.apache.cxf.sts.token.provider.TokenProvider;
 import org.apache.cxf.sts.token.provider.TokenProviderParameters;
 import org.apache.cxf.sts.token.provider.TokenProviderResponse;
+import org.apache.cxf.sts.token.provider.jwt.DefaultJWTClaimsProvider;
+import org.apache.cxf.sts.token.provider.jwt.JWTClaimsProvider;
+import org.apache.cxf.sts.token.provider.jwt.JWTClaimsProviderParameters;
 import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider;
+import org.apache.cxf.sts.token.validator.jwt.DefaultJWTRoleParser;
 import org.apache.cxf.sts.token.validator.jwt.JWTTokenValidator;
 import org.apache.cxf.ws.security.tokenstore.TokenStore;
 import org.apache.wss4j.common.crypto.Crypto;
@@ -136,6 +142,182 @@ public class JWTTokenValidatorTest extends org.junit.Assert {
         assertTrue(validatorResponse.getToken().getState() == STATE.INVALID);
     }
     
+    @org.junit.Test
+    public void testUnsignedToken() throws Exception {
+        // Create
+        TokenProvider jwtTokenProvider = new JWTTokenProvider();
+        ((JWTTokenProvider)jwtTokenProvider).setSignToken(false);
+        
+        TokenProviderParameters providerParameters = createProviderParameters();
+        Crypto crypto = CryptoFactory.getInstance(getEveCryptoProperties());
+        CallbackHandler callbackHandler = new EveCallbackHandler();
+        providerParameters.getStsProperties().setSignatureCrypto(crypto);
+        providerParameters.getStsProperties().setCallbackHandler(callbackHandler);
+        providerParameters.getStsProperties().setSignatureUsername("eve");
+        
+        assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE));
+        TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters);
+        assertTrue(providerResponse != null);
+        assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId()
!= null);
+
+        String token = (String)providerResponse.getToken();
+        assertNotNull(token);
+        assertTrue(token.split("\\.").length == 2);
+        
+        // Validate the token
+        TokenValidator jwtTokenValidator = new JWTTokenValidator();
+        TokenValidatorParameters validatorParameters = createValidatorParameters();
+        TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements();
+        
+        // Create a ValidateTarget consisting of a JWT Token
+        ReceivedToken validateTarget = new ReceivedToken(token);
+        tokenRequirements.setValidateTarget(validateTarget);
+        validatorParameters.setToken(validateTarget);
+        
+        assertTrue(jwtTokenValidator.canHandleToken(validateTarget));
+        
+        TokenValidatorResponse validatorResponse = 
+            jwtTokenValidator.validateToken(validatorParameters);
+        assertTrue(validatorResponse != null);
+        assertTrue(validatorResponse.getToken() != null);
+        assertTrue(validatorResponse.getToken().getState() == STATE.INVALID);
+    }
+    
+    @org.junit.Test
+    public void testInvalidConditionJWT() throws Exception {
+        // Create
+        TokenProvider jwtTokenProvider = new JWTTokenProvider();
+        ((JWTTokenProvider)jwtTokenProvider).setSignToken(true);
+        
+        DefaultJWTClaimsProvider jwtClaimsProvider = new DefaultJWTClaimsProvider();
+        jwtClaimsProvider.setLifetime(1L);
+        ((JWTTokenProvider)jwtTokenProvider).setJwtClaimsProvider(jwtClaimsProvider);
+        
+        TokenProviderParameters providerParameters = createProviderParameters();
+        
+        assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE));
+        TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters);
+        assertTrue(providerResponse != null);
+        assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId()
!= null);
+
+        String token = (String)providerResponse.getToken();
+        assertNotNull(token);
+        assertTrue(token.split("\\.").length == 3);
+        
+        Thread.sleep(1500L);
+        
+        // Validate the token
+        TokenValidator jwtTokenValidator = new JWTTokenValidator();
+        TokenValidatorParameters validatorParameters = createValidatorParameters();
+        TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements();
+        
+        // Create a ValidateTarget consisting of a JWT Token
+        ReceivedToken validateTarget = new ReceivedToken(token);
+        tokenRequirements.setValidateTarget(validateTarget);
+        validatorParameters.setToken(validateTarget);
+        
+        assertTrue(jwtTokenValidator.canHandleToken(validateTarget));
+        
+        TokenValidatorResponse validatorResponse = 
+            jwtTokenValidator.validateToken(validatorParameters);
+        assertTrue(validatorResponse != null);
+        assertTrue(validatorResponse.getToken() != null);
+        assertTrue(validatorResponse.getToken().getState() == STATE.INVALID);
+    }
+    
+    @org.junit.Test
+    public void testChangedSignature() throws Exception {
+        // Create
+        TokenProvider jwtTokenProvider = new JWTTokenProvider();
+        ((JWTTokenProvider)jwtTokenProvider).setSignToken(true);
+        
+        DefaultJWTClaimsProvider jwtClaimsProvider = new DefaultJWTClaimsProvider();
+        jwtClaimsProvider.setLifetime(1L);
+        ((JWTTokenProvider)jwtTokenProvider).setJwtClaimsProvider(jwtClaimsProvider);
+        
+        TokenProviderParameters providerParameters = createProviderParameters();
+        
+        assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE));
+        TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters);
+        assertTrue(providerResponse != null);
+        assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId()
!= null);
+
+        String token = (String)providerResponse.getToken();
+        // Change the signature
+        token += "blah";
+        assertNotNull(token);
+        assertTrue(token.split("\\.").length == 3);
+        
+        Thread.sleep(1500L);
+        
+        // Validate the token
+        TokenValidator jwtTokenValidator = new JWTTokenValidator();
+        TokenValidatorParameters validatorParameters = createValidatorParameters();
+        TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements();
+        
+        // Create a ValidateTarget consisting of a JWT Token
+        ReceivedToken validateTarget = new ReceivedToken(token);
+        tokenRequirements.setValidateTarget(validateTarget);
+        validatorParameters.setToken(validateTarget);
+        
+        assertTrue(jwtTokenValidator.canHandleToken(validateTarget));
+        
+        TokenValidatorResponse validatorResponse = 
+            jwtTokenValidator.validateToken(validatorParameters);
+        assertTrue(validatorResponse != null);
+        assertTrue(validatorResponse.getToken() != null);
+        assertTrue(validatorResponse.getToken().getState() == STATE.INVALID);
+    }
+    
+    @org.junit.Test
+    public void testJWTWithRoles() throws Exception {
+        // Create
+        TokenProvider jwtTokenProvider = new JWTTokenProvider();
+        ((JWTTokenProvider)jwtTokenProvider).setSignToken(true);
+        
+        JWTClaimsProvider claimsProvider = new RoleJWTClaimsProvider("manager");
+        ((JWTTokenProvider)jwtTokenProvider).setJwtClaimsProvider(claimsProvider);
+        
+        TokenProviderParameters providerParameters = createProviderParameters();
+        
+        assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE));
+        TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters);
+        assertTrue(providerResponse != null);
+        assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId()
!= null);
+
+        String token = (String)providerResponse.getToken();
+        assertNotNull(token);
+        assertTrue(token.split("\\.").length == 3);
+        
+        // Validate the token
+        TokenValidator jwtTokenValidator = new JWTTokenValidator();
+        // Set the role
+        DefaultJWTRoleParser roleParser = new DefaultJWTRoleParser();
+        roleParser.setRoleClaim("role");
+        ((JWTTokenValidator)jwtTokenValidator).setRoleParser(roleParser);
+        TokenValidatorParameters validatorParameters = createValidatorParameters();
+        TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements();
+        
+        // Create a ValidateTarget consisting of a JWT Token
+        ReceivedToken validateTarget = new ReceivedToken(token);
+        tokenRequirements.setValidateTarget(validateTarget);
+        validatorParameters.setToken(validateTarget);
+        
+        assertTrue(jwtTokenValidator.canHandleToken(validateTarget));
+        
+        TokenValidatorResponse validatorResponse = 
+            jwtTokenValidator.validateToken(validatorParameters);
+        assertTrue(validatorResponse != null);
+        assertTrue(validatorResponse.getToken() != null);
+        assertTrue(validatorResponse.getToken().getState() == STATE.VALID);
+        
+        Principal principal = validatorResponse.getPrincipal();
+        assertTrue(principal != null && principal.getName() != null);
+        Set<Principal> roles = validatorResponse.getRoles();
+        assertTrue(roles != null && !roles.isEmpty());
+        assertTrue(roles.iterator().next().getName().equals("manager"));
+    }
+    
     private TokenProviderParameters createProviderParameters() throws WSSecurityException
{
         TokenProviderParameters parameters = new TokenProviderParameters();
         
@@ -243,4 +425,20 @@ public class JWTTokenValidatorTest extends org.junit.Assert {
             }
         }
     }
+    
+    private static class RoleJWTClaimsProvider extends DefaultJWTClaimsProvider {
+        
+        private String role;
+        
+        public RoleJWTClaimsProvider(String role) {
+            this.role = role;
+        }
+        
+        @Override
+        public JwtClaims getJwtClaims(JWTClaimsProviderParameters jwtClaimsProviderParameters)
{
+            JwtClaims claims = super.getJwtClaims(jwtClaimsProviderParameters);
+            claims.setProperty("role", role);
+            return claims;
+        }
+    }
 }


Mime
View raw message