keycloak-uncached

Merge pull request #1297 from AOEpeople/KEYCLOAK-1350 KEYCLOAK-1350

6/3/2015 4:15:50 AM

Details

diff --git a/events/api/src/main/java/org/keycloak/events/Details.java b/events/api/src/main/java/org/keycloak/events/Details.java
index cee7475..1a9c479 100755
--- a/events/api/src/main/java/org/keycloak/events/Details.java
+++ b/events/api/src/main/java/org/keycloak/events/Details.java
@@ -24,5 +24,7 @@ public interface Details {
     String NODE_HOST = "node_host";
     String REASON = "reason";
     String REVOKED_CLIENT = "revoked_client";
+    String CLIENT_SESSION_STATE = "client_session_state";
+    String CLIENT_SESSION_HOST = "client_session_host";
 
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index ffbc6a7..e29d5d4 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -37,6 +37,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -227,16 +228,7 @@ public class TokenEndpoint {
             throw new ErrorResponseException("invalid_grant", "Session not active", Response.Status.BAD_REQUEST);
         }
 
-        String adapterSessionId = formParams.getFirst(AdapterConstants.CLIENT_SESSION_STATE);
-        if (adapterSessionId != null) {
-            String adapterSessionHost = formParams.getFirst(AdapterConstants.CLIENT_SESSION_HOST);
-            logger.debugf("Adapter Session '%s' saved in ClientSession for client '%s'. Host is '%s'", adapterSessionId, client.getClientId(), adapterSessionHost);
-
-            event.detail(AdapterConstants.CLIENT_SESSION_STATE, adapterSessionId);
-            clientSession.setNote(AdapterConstants.CLIENT_SESSION_STATE, adapterSessionId);
-            event.detail(AdapterConstants.CLIENT_SESSION_HOST, adapterSessionHost);
-            clientSession.setNote(AdapterConstants.CLIENT_SESSION_HOST, adapterSessionHost);
-        }
+        updateClientSession(clientSession);
 
         AccessToken token = tokenManager.createClientAccessToken(session, accessCode.getRequestedRoles(), realm, client, user, userSession, clientSession);
 
@@ -259,6 +251,10 @@ public class TokenEndpoint {
         AccessTokenResponse res;
         try {
             res = tokenManager.refreshAccessToken(session, uriInfo, clientConnection, realm, client, refreshToken, event, headers);
+
+            UserSessionModel userSession = session.sessions().getUserSession(realm, res.getSessionState());
+            updateClientSessions(userSession.getClientSessions());
+
         } catch (OAuthErrorException e) {
             event.error(Errors.INVALID_TOKEN);
             throw new ErrorResponseException(e.getError(), e.getDescription(), Response.Status.BAD_REQUEST);
@@ -269,6 +265,45 @@ public class TokenEndpoint {
         return Cors.add(request, Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
     }
 
+    private void updateClientSession(ClientSessionModel clientSession) {
+
+        if(clientSession == null) {
+            logger.error("client session is null");
+            return;
+        }
+
+        String adapterSessionId = formParams.getFirst(AdapterConstants.CLIENT_SESSION_STATE);
+        if (adapterSessionId != null) {
+            String adapterSessionHost = formParams.getFirst(AdapterConstants.CLIENT_SESSION_HOST);
+            logger.debugf("Adapter Session '%s' saved in ClientSession for client '%s'. Host is '%s'", adapterSessionId, client.getClientId(), adapterSessionHost);
+
+            event.detail(AdapterConstants.CLIENT_SESSION_STATE, adapterSessionId);
+            clientSession.setNote(AdapterConstants.CLIENT_SESSION_STATE, adapterSessionId);
+            event.detail(AdapterConstants.CLIENT_SESSION_HOST, adapterSessionHost);
+            clientSession.setNote(AdapterConstants.CLIENT_SESSION_HOST, adapterSessionHost);
+        }
+    }
+
+    private void updateClientSessions(List<ClientSessionModel> clientSessions) {
+        if(clientSessions == null) {
+            logger.error("client sessions is null");
+            return;
+        }
+        for (ClientSessionModel clientSession : clientSessions) {
+            if(clientSession == null) {
+                logger.error("client session is null");
+                continue;
+            }
+            if(clientSession.getClient() == null) {
+                logger.error("client model in client session is null");
+                continue;
+            }
+            if(client.getId().equals(clientSession.getClient().getId())) {
+                updateClientSession(clientSession);
+            }
+        }
+    }
+
     public Response buildResourceOwnerPasswordCredentialsGrant() {
         if (!realm.isPasswordCredentialGrantAllowed()) {
             throw new ErrorResponseException("not_enabled", "Direct Grant REST API not enabled", Response.Status.FORBIDDEN);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthDanceClientSessionExtensionTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthDanceClientSessionExtensionTest.java
new file mode 100644
index 0000000..f097892
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthDanceClientSessionExtensionTest.java
@@ -0,0 +1,71 @@
+package org.keycloak.testsuite.oauth;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.events.Details;
+import org.keycloak.events.Event;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+
+/**
+ * @author Sebastian Rose, AOE on 02.06.15.
+ */
+public class OAuthDanceClientSessionExtensionTest {
+
+    @ClassRule
+    public static KeycloakRule keycloakRule = new KeycloakRule();
+
+    @Rule
+    public WebRule webRule = new WebRule(this);
+
+    @WebResource
+    protected OAuthClient oauth;
+
+    @Rule
+    public AssertEvents events = new AssertEvents(keycloakRule);
+
+    @Test
+    public void doOauthDanceWithClientSessionStateAndHost() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+
+        Event loginEvent = events.expectLogin().assertEvent();
+
+        String sessionId = loginEvent.getSessionId();
+        String codeId = loginEvent.getDetails().get(Details.CODE_ID);
+
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+
+        String clientSessionState = "1234";
+        String clientSessionHost = "test-client-host";
+
+        OAuthClient.AccessTokenResponse tokenResponse = oauth.clientSessionState(clientSessionState)
+                                                             .clientSessionHost(clientSessionHost)
+                                                             .doAccessTokenRequest(code, "password");
+
+        String refreshTokenString = tokenResponse.getRefreshToken();
+
+        Event tokenEvent = events.expectCodeToToken(codeId, sessionId)
+                                 .detail(Details.CLIENT_SESSION_STATE, clientSessionState)
+                                 .detail(Details.CLIENT_SESSION_HOST, clientSessionHost)
+                                 .assertEvent();
+
+
+        String updatedClientSessionState = "5678";
+
+        oauth.clientSessionState(updatedClientSessionState)
+             .clientSessionHost(clientSessionHost)
+             .doRefreshTokenRequest(refreshTokenString, "password");
+
+        events.expectRefresh(tokenEvent.getDetails().get(Details.REFRESH_TOKEN_ID), sessionId)
+              .detail(Details.CLIENT_SESSION_STATE, updatedClientSessionState)
+              .detail(Details.CLIENT_SESSION_HOST, clientSessionHost)
+              .assertEvent();
+
+    }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
index 3c81b8a..4e4f57a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -35,6 +35,7 @@ import org.junit.Assert;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.RSATokenVerifier;
 import org.keycloak.VerificationException;
+import org.keycloak.constants.AdapterConstants;
 import org.keycloak.freemarker.LocaleHelper;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
@@ -78,6 +79,10 @@ public class OAuthClient {
 
     private PublicKey realmPublicKey;
 
+    private String clientSessionState;
+
+    private String clientSessionHost;
+
     public OAuthClient(WebDriver driver) {
         this.driver = driver;
 
@@ -128,6 +133,14 @@ public class OAuthClient {
             parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId));
         }
 
+        if(clientSessionState != null) {
+            parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_STATE, clientSessionState));
+        }
+
+        if(clientSessionHost != null) {
+            parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, clientSessionHost));
+        }
+
         UrlEncodedFormEntity formEntity = null;
         try {
             formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
@@ -155,6 +168,13 @@ public class OAuthClient {
         parameters.add(new BasicNameValuePair("username", username));
         parameters.add(new BasicNameValuePair("password", password));
 
+        if(clientSessionState != null) {
+            parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_STATE, clientSessionState));
+        }
+        if(clientSessionHost != null) {
+            parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, clientSessionHost));
+        }
+
         UrlEncodedFormEntity formEntity;
         try {
             formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
@@ -211,6 +231,13 @@ public class OAuthClient {
             parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId));
         }
 
+        if(clientSessionState != null) {
+            parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_STATE, clientSessionState));
+        }
+        if(clientSessionHost != null) {
+            parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, clientSessionHost));
+        }
+
         UrlEncodedFormEntity formEntity;
         try {
             formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
@@ -360,6 +387,16 @@ public class OAuthClient {
         return this;
     }
 
+    public OAuthClient clientSessionState(String client_session_state) {
+        this.clientSessionState = client_session_state;
+        return this;
+    }
+
+    public OAuthClient clientSessionHost(String client_session_host) {
+        this.clientSessionHost = client_session_host;
+        return this;
+    }
+
     public String getRealm() {
         return realm;
     }