keycloak-aplcache

Ensure that KeycloakUndertowAccount and referenced classes

10/1/2014 2:08:41 PM

Details

diff --git a/core/src/main/java/org/keycloak/KeycloakPrincipal.java b/core/src/main/java/org/keycloak/KeycloakPrincipal.java
index ca05f79..e2819ea 100755
--- a/core/src/main/java/org/keycloak/KeycloakPrincipal.java
+++ b/core/src/main/java/org/keycloak/KeycloakPrincipal.java
@@ -7,16 +7,16 @@ import java.security.Principal;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class KeycloakPrincipal implements Principal, Serializable {
+public class KeycloakPrincipal<T extends KeycloakSecurityContext> implements Principal, Serializable {
     protected final String name;
-    protected final KeycloakSecurityContext context;
+    protected final T context;
 
-    public KeycloakPrincipal(String name, KeycloakSecurityContext context) {
+    public KeycloakPrincipal(String name, T context) {
         this.name = name;
         this.context = context;
     }
 
-    public KeycloakSecurityContext getKeycloakSecurityContext() {
+    public T getKeycloakSecurityContext() {
         return context;
     }
 
diff --git a/core/src/main/java/org/keycloak/KeycloakSecurityContext.java b/core/src/main/java/org/keycloak/KeycloakSecurityContext.java
index bea9d9f..4658c68 100755
--- a/core/src/main/java/org/keycloak/KeycloakSecurityContext.java
+++ b/core/src/main/java/org/keycloak/KeycloakSecurityContext.java
@@ -2,7 +2,12 @@ package org.keycloak;
 
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.IDToken;
+import org.keycloak.util.Base64Url;
+import org.keycloak.util.JsonSerialization;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.Serializable;
 
 /**
@@ -11,10 +16,12 @@ import java.io.Serializable;
  */
 public class KeycloakSecurityContext implements Serializable {
     protected String tokenString;
-    protected AccessToken token;
-    protected IDToken idToken;
     protected String idTokenString;
 
+    // Don't store parsed tokens into HTTP session
+    protected transient AccessToken token;
+    protected transient IDToken idToken;
+
     public KeycloakSecurityContext() {
     }
 
@@ -40,4 +47,32 @@ public class KeycloakSecurityContext implements Serializable {
     public String getIdTokenString() {
         return idTokenString;
     }
+
+
+    // SERIALIZATION
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+
+        token = parseToken(tokenString, AccessToken.class);
+        idToken = parseToken(idTokenString, IDToken.class);
+    }
+
+    // Just decode token without any verifications
+    private <T> T parseToken(String encoded, Class<T> clazz) throws IOException {
+        if (encoded == null)
+            return null;
+
+        String[] parts = encoded.split("\\.");
+        if (parts.length < 2 || parts.length > 3) throw new IllegalArgumentException("Parsing error");
+
+        byte[] bytes = Base64Url.decode(parts[1]);
+        return JsonSerialization.readValue(bytes, clazz);
+    }
+
+
 }
diff --git a/core/src/main/java/org/keycloak/representations/AccessToken.java b/core/src/main/java/org/keycloak/representations/AccessToken.java
index 262d07d..a9eaf3e 100755
--- a/core/src/main/java/org/keycloak/representations/AccessToken.java
+++ b/core/src/main/java/org/keycloak/representations/AccessToken.java
@@ -3,6 +3,7 @@ package org.keycloak.representations;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonProperty;
 
+import java.io.Serializable;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -13,7 +14,7 @@ import java.util.Set;
  * @version $Revision: 1 $
  */
 public class AccessToken extends IDToken {
-    public static class Access {
+    public static class Access implements Serializable {
         @JsonProperty("roles")
         protected Set<String> roles;
         @JsonProperty("verify_caller")
diff --git a/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java b/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
index cd948b8..f3a89b6 100755
--- a/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
+++ b/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
@@ -1,13 +1,18 @@
 package org.keycloak;
 
-import junit.framework.Assert;
+import org.junit.Assert;
 import org.junit.Test;
 import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.IDToken;
 import org.keycloak.util.JsonSerialization;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 
@@ -18,10 +23,7 @@ import java.security.KeyPairGenerator;
 public class SkeletonKeyTokenTest {
     @Test
     public void testToken() throws Exception {
-        AccessToken token = new AccessToken();
-        token.id("111");
-        token.addAccess("foo").addRole("admin");
-        token.addAccess("bar").addRole("user");
+        AccessToken token = createSimpleToken();
 
         String json = JsonSerialization.writeValueAsString(token);
         token = JsonSerialization.readValue(json, AccessToken.class);
@@ -34,7 +36,7 @@ public class SkeletonKeyTokenTest {
 
     @Test
     public void testRSA() throws Exception {
-        AccessToken token = new AccessToken();
+        AccessToken token = createSimpleToken();
         token.id("111");
         token.addAccess("foo").addRole("admin");
         token.addAccess("bar").addRole("user");
@@ -51,4 +53,57 @@ public class SkeletonKeyTokenTest {
         Assert.assertEquals("111", token.getId());
         Assert.assertTrue(RSAProvider.verify(input, keyPair.getPublic()));
     }
+
+    @Test
+    public void testSerialization() throws Exception {
+        AccessToken token = createSimpleToken();
+        IDToken idToken = new IDToken();
+        idToken.setEmail("joe@email.cz");
+
+        KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+
+        String encoded = new JWSBuilder()
+                .jsonContent(token)
+                .rsa256(keyPair.getPrivate());
+        String encodedIdToken = new JWSBuilder()
+                .jsonContent(idToken)
+                .rsa256(keyPair.getPrivate());
+
+        KeycloakSecurityContext ctx = new KeycloakSecurityContext(encoded, token, encodedIdToken, idToken);
+        KeycloakPrincipal principal = new KeycloakPrincipal("joe", ctx);
+
+        // Serialize
+        ByteArrayOutputStream bso = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(bso);
+        oos.writeObject(principal);
+        oos.close();
+
+        // Deserialize
+        byte[] bytes = bso.toByteArray();
+        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+        ObjectInputStream ois = new ObjectInputStream(bis);
+        principal = (KeycloakPrincipal)ois.readObject();
+        ctx = principal.getKeycloakSecurityContext();
+        token = ctx.getToken();
+        idToken = ctx.getIdToken();
+
+        System.out.println("Size of serialized principal: " + bytes.length);
+
+        Assert.assertEquals(encoded, ctx.getTokenString());
+        Assert.assertEquals(encodedIdToken, ctx.getIdTokenString());
+        Assert.assertEquals("111", token.getId());
+        Assert.assertEquals("111", token.getId());
+        Assert.assertTrue(token.getResourceAccess("foo").isUserInRole("admin"));
+        Assert.assertTrue(token.getResourceAccess("bar").isUserInRole("user"));
+        Assert.assertEquals("joe@email.cz", idToken.getEmail());
+        ois.close();
+    }
+
+    private AccessToken createSimpleToken() {
+        AccessToken token = new AccessToken();
+        token.id("111");
+        token.addAccess("foo").addRole("admin");
+        token.addAccess("bar").addRole("user");
+        return token;
+    }
 }
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
index 7073987..3cf2c91 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
@@ -104,18 +104,18 @@ public abstract class RequestAuthenticator {
 
     protected void completeAuthentication(OAuthRequestAuthenticator oauth) {
         RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
-        final KeycloakPrincipal principal = new KeycloakPrincipal(oauth.getToken().getSubject(), session);
-        completeOAuthAuthentication(principal, session);
+        final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(oauth.getToken().getSubject(), session);
+        completeOAuthAuthentication(principal);
     }
 
-    protected abstract void completeOAuthAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session);
-    protected abstract void completeBearerAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session);
+    protected abstract void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal);
+    protected abstract void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal);
     protected abstract boolean isCached();
 
     protected void completeAuthentication(BearerTokenRequestAuthenticator bearer) {
         RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, bearer.getTokenString(), bearer.getToken(), null, null, null);
-        final KeycloakPrincipal principal = new KeycloakPrincipal(bearer.getToken().getSubject(), session);
-        completeBearerAuthentication(principal, session);
+        final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(bearer.getToken().getSubject(), session);
+        completeBearerAuthentication(principal);
     }
 
 }
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java
index 4be97da..aca4db7 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java
@@ -53,7 +53,8 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
     }
 
     @Override
-    protected void completeOAuthAuthentication(KeycloakPrincipal skp, RefreshableKeycloakSecurityContext securityContext) {
+    protected void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> skp) {
+        RefreshableKeycloakSecurityContext securityContext = skp.getKeycloakSecurityContext();
         request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
         Set<String> roles = getRolesFromToken(securityContext);
         GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skp, roles, securityContext);
@@ -67,7 +68,8 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
     }
 
     @Override
-    protected void completeBearerAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext securityContext) {
+    protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+        RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
         Set<String> roles = getRolesFromToken(securityContext);
         for (String role : roles) {
             log.info("Bearer role: " + role);
diff --git a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
index 6ba9984..5d700c1 100755
--- a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
+++ b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
@@ -74,7 +74,7 @@ public class JaxrsBearerTokenFilter implements ContainerRequestFilter {
             KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null);
             ResteasyProviderFactory.pushContext(KeycloakSecurityContext.class, skSession);
 
-            final KeycloakPrincipal principal = new KeycloakPrincipal(token.getSubject(), skSession);
+            final KeycloakPrincipal<KeycloakSecurityContext> principal = new KeycloakPrincipal<KeycloakSecurityContext>(token.getSubject(), skSession);
             final boolean isSecure = securityContext.isSecure();
             final AccessToken.Access access;
             if (resourceName != null) {
diff --git a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaRequestAuthenticator.java b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaRequestAuthenticator.java
index 4738443..6a05be8 100755
--- a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaRequestAuthenticator.java
+++ b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaRequestAuthenticator.java
@@ -53,7 +53,8 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
     }
 
     @Override
-    protected void completeOAuthAuthentication(KeycloakPrincipal skp, RefreshableKeycloakSecurityContext securityContext) {
+    protected void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> skp) {
+        RefreshableKeycloakSecurityContext securityContext = skp.getKeycloakSecurityContext();
         request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
     	Set<String> roles = getRolesFromToken(securityContext);
         GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skp, roles, securityContext);
@@ -67,7 +68,8 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
     }
 
     @Override
-    protected void completeBearerAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext securityContext) {
+    protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+        RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
         Set<String> roles = getRolesFromToken(securityContext);
         for (String role : roles) {
             log.info("Bearer role: " + role);
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakUndertowAccount.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakUndertowAccount.java
index 02680f4..58cbb02 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakUndertowAccount.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakUndertowAccount.java
@@ -35,18 +35,17 @@ import java.util.Set;
 */
 public class KeycloakUndertowAccount implements Account, Serializable, KeycloakAccount {
     protected static Logger log = Logger.getLogger(KeycloakUndertowAccount.class);
-    protected RefreshableKeycloakSecurityContext session;
-    protected KeycloakPrincipal principal;
+    protected KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal;
     protected Set<String> accountRoles;
 
-    public KeycloakUndertowAccount(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session, KeycloakDeployment deployment) {
+    public KeycloakUndertowAccount(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
         this.principal = principal;
-        this.session = session;
-        setRoles(session.getToken());
+        setRoles(principal.getKeycloakSecurityContext().getToken());
     }
 
     protected void setRoles(AccessToken accessToken) {
         Set<String> roles = null;
+        RefreshableKeycloakSecurityContext session = getKeycloakSecurityContext();
         if (session.getDeployment().isUseResourceRoleMappings()) {
             if (log.isTraceEnabled()) {
                 log.trace("useResourceRoleMappings");
@@ -61,12 +60,13 @@ public class KeycloakUndertowAccount implements Account, Serializable, KeycloakA
             if (access != null) roles = access.getRoles();
         }
         if (roles == null) roles = Collections.emptySet();
-        /*
-        log.info("Setting roles: ");
-        for (String role : roles) {
-            log.info("   role: " + role);
+        if (log.isTraceEnabled()) {
+            log.trace("Setting roles: ");
+            for (String role : roles) {
+                log.trace("   role: " + role);
+            }
         }
-        */
+
         this.accountRoles = roles;
     }
 
@@ -82,15 +82,16 @@ public class KeycloakUndertowAccount implements Account, Serializable, KeycloakA
 
     @Override
     public RefreshableKeycloakSecurityContext getKeycloakSecurityContext() {
-        return session;
+        return principal.getKeycloakSecurityContext();
     }
 
     public void setDeployment(KeycloakDeployment deployment) {
-        session.setDeployment(deployment);
+        principal.getKeycloakSecurityContext().setDeployment(deployment);
     }
 
     public boolean isActive() {
         // this object may have been serialized, so we need to reset realm config/metadata
+        RefreshableKeycloakSecurityContext session = getKeycloakSecurityContext();
         if (session.isActive()) {
             log.debug("session is active");
             return true;
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletRequestAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletRequestAuthenticator.java
index a8365cd..c46f477 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletRequestAuthenticator.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletRequestAuthenticator.java
@@ -88,7 +88,7 @@ public class ServletRequestAuthenticator extends UndertowRequestAuthenticator {
     }
 
     @Override
-    protected KeycloakUndertowAccount createAccount(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session) {
-        return new KeycloakUndertowAccount(principal, session, deployment);
+    protected KeycloakUndertowAccount createAccount(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+        return new KeycloakUndertowAccount(principal);
     }
 }
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowRequestAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowRequestAuthenticator.java
index 67a7b7a..79435ae 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowRequestAuthenticator.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowRequestAuthenticator.java
@@ -63,8 +63,8 @@ public abstract class UndertowRequestAuthenticator extends RequestAuthenticator 
     }
 
     @Override
-    protected void completeOAuthAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session) {
-        KeycloakUndertowAccount account = createAccount(principal, session);
+    protected void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+        KeycloakUndertowAccount account = createAccount(principal);
         securityContext.authenticationComplete(account, "KEYCLOAK", false);
         propagateKeycloakContext(account);
         login(account);
@@ -80,8 +80,8 @@ public abstract class UndertowRequestAuthenticator extends RequestAuthenticator 
 
 
     @Override
-    protected void completeBearerAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session) {
-        KeycloakUndertowAccount account = createAccount(principal, session);
+    protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+        KeycloakUndertowAccount account = createAccount(principal);
         securityContext.authenticationComplete(account, "KEYCLOAK", false);
         propagateKeycloakContext(account);
     }
@@ -114,5 +114,5 @@ public abstract class UndertowRequestAuthenticator extends RequestAuthenticator 
      * Subclasses need to be able to create their own version of the KeycloakUndertowAccount
      * @return The account
      */
-    protected abstract KeycloakUndertowAccount createAccount(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session);
+    protected abstract KeycloakUndertowAccount createAccount(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal);
 }