keycloak-aplcache

Merge pull request #3579 from mposolda/master KEYCLOAK-3824

12/1/2016 2:45:55 PM

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
index 0b33294..dd0d39b 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
@@ -343,6 +343,12 @@ public class AdapterDeploymentContext {
         }
 
         @Override
+        public void updateNotBefore(int notBefore) {
+            delegate.setNotBefore(notBefore);
+            getPublicKeyLocator().reset(this);
+        }
+
+        @Override
         public void setExposeToken(boolean exposeToken) {
             delegate.setExposeToken(exposeToken);
         }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
index 3f98a68..b9ee4c6 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
@@ -329,6 +329,11 @@ public class KeycloakDeployment {
         this.notBefore = notBefore;
     }
 
+    public void updateNotBefore(int notBefore) {
+        this.notBefore = notBefore;
+        getPublicKeyLocator().reset(this);
+    }
+
     public boolean isAlwaysRefreshToken() {
         return alwaysRefreshToken;
     }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
index 109361f..e8f5344 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
@@ -357,7 +357,7 @@ public class OAuthRequestAuthenticator {
             return challenge(403, OIDCAuthenticationError.Reason.INVALID_TOKEN, null);
         }
         if (tokenResponse.getNotBeforePolicy() > deployment.getNotBefore()) {
-            deployment.setNotBefore(tokenResponse.getNotBeforePolicy());
+            deployment.updateNotBefore(tokenResponse.getNotBeforePolicy());
         }
         if (token.getIssuedAt() < deployment.getNotBefore()) {
             log.error("Stale token");
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java
index 9a291c0..b4d017b 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java
@@ -155,7 +155,7 @@ public class PreAuthActionsHandler {
             } else {
                 log.debugf("logout of all sessions for application '%s'", action.getResource());
                 if (action.getNotBefore() > deployment.getNotBefore()) {
-                    deployment.setNotBefore(action.getNotBefore());
+                    deployment.updateNotBefore(action.getNotBefore());
                 }
                 userSessionManagement.logoutAll();
             }
@@ -177,7 +177,7 @@ public class PreAuthActionsHandler {
             }
             PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
             if (!validateAction(action)) return;
-            deployment.setNotBefore(action.getNotBefore());
+            deployment.updateNotBefore(action.getNotBefore());
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
index 39a3f1e..c70bce1 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
@@ -144,7 +144,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext 
         }
 
         if (response.getNotBeforePolicy() > deployment.getNotBefore()) {
-            deployment.setNotBefore(response.getNotBeforePolicy());
+            deployment.updateNotBefore(response.getNotBeforePolicy());
         }
 
         this.token = token;
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.java
index 2aa51a4..9e285a2 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.java
@@ -37,4 +37,9 @@ public class HardcodedPublicKeyLocator implements PublicKeyLocator {
     public PublicKey getPublicKey(String kid, KeycloakDeployment deployment) {
         return publicKey;
     }
+
+    @Override
+    public void reset(KeycloakDeployment deployment) {
+
+    }
 }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java
index 45f420c..22c6d7d 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java
@@ -57,20 +57,24 @@ public class JWKPublicKeyLocator implements PublicKeyLocator {
         }
 
         // Check if we are allowed to send request
-        if (currentTime > lastRequestTime + minTimeBetweenRequests) {
-            synchronized (this) {
-                currentTime = Time.currentTime();
-                if (currentTime > lastRequestTime + minTimeBetweenRequests) {
-                    sendRequest(deployment);
-                    lastRequestTime = currentTime;
-                } else {
-                    log.debugf("Won't send request to realm jwks url. Last request time was %d", lastRequestTime);
-                }
+        synchronized (this) {
+            currentTime = Time.currentTime();
+            if (currentTime > lastRequestTime + minTimeBetweenRequests) {
+                sendRequest(deployment);
+                lastRequestTime = currentTime;
+            } else {
+                log.debugf("Won't send request to realm jwks url. Last request time was %d", lastRequestTime);
             }
+
+            return lookupCachedKey(publicKeyCacheTtl, currentTime, kid);
         }
+    }
 
-        return lookupCachedKey(publicKeyCacheTtl, currentTime, kid);
 
+    @Override
+    public void reset(KeycloakDeployment deployment) {
+        sendRequest(deployment);
+        lastRequestTime = Time.currentTime();
     }
 
 
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java
index 3efd90a..096f75f 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java
@@ -34,4 +34,11 @@ public interface PublicKeyLocator {
      */
     PublicKey getPublicKey(String kid, KeycloakDeployment deployment);
 
+    /**
+     * Reset the state of locator (eg. clear the cached keys)
+     *
+     * @param deployment
+     */
+    void reset(KeycloakDeployment deployment);
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOIDCPublicKeyRotationAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOIDCPublicKeyRotationAdapterTest.java
index d9116ce..8c42b99 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOIDCPublicKeyRotationAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOIDCPublicKeyRotationAdapterTest.java
@@ -187,8 +187,6 @@ public class AbstractOIDCPublicKeyRotationAdapterTest extends AbstractServletsAd
     // KEYCLOAK-3824: Test for public-key-cache-ttl
     @Test
     public void testPublicKeyCacheTtl() {
-        driver.manage().timeouts().pageLoadTimeout(1000, TimeUnit.SECONDS);
-
         // increase accessTokenLifespan to 1200
         RealmRepresentation demoRealm = adminClient.realm(DEMO).toRepresentation();
         demoRealm.setAccessTokenLifespan(1200);
@@ -202,8 +200,10 @@ public class AbstractOIDCPublicKeyRotationAdapterTest extends AbstractServletsAd
         int status = invokeRESTEndpoint(accessTokenString);
         Assert.assertEquals(200, status);
 
-        // Invalidate realm public key
+        // Re-generate realm public key and remove the old key
+        String oldKeyId = getActiveKeyId();
         generateNewRealmKey();
+        adminClient.realm(DEMO).components().component(oldKeyId).remove();
 
         // Send REST request to the customer-db app. Should be still succcessfully authenticated as the JWKPublicKeyLocator cache is still valid
         status = invokeRESTEndpoint(accessTokenString);
@@ -225,6 +225,8 @@ public class AbstractOIDCPublicKeyRotationAdapterTest extends AbstractServletsAd
     // KEYCLOAK-3823: Test that sending notBefore policy invalidates JWKPublicKeyLocator cache
     @Test
     public void testPublicKeyCacheInvalidatedWhenPushedNotBefore() {
+        driver.manage().timeouts().pageLoadTimeout(1000, TimeUnit.SECONDS);
+
         // increase accessTokenLifespan to 1200
         RealmRepresentation demoRealm = adminClient.realm(DEMO).toRepresentation();
         demoRealm.setAccessTokenLifespan(1200);
@@ -234,20 +236,20 @@ public class AbstractOIDCPublicKeyRotationAdapterTest extends AbstractServletsAd
         loginToTokenMinTtlApp();
         String accessTokenString = tokenMinTTLPage.getAccessTokenString();
 
-        // Send REST request to customer-db app. I should be successfully authenticated
+        // Generate new realm public key
+        String oldKeyId = getActiveKeyId();
+        generateNewRealmKey();
+
+        // Send REST request to customer-db app. It should be successfully authenticated even that token is signed by the old key
         int status = invokeRESTEndpoint(accessTokenString);
         Assert.assertEquals(200, status);
 
-        // Invalidate realm public key
-        generateNewRealmKey();
+        // Remove the old realm key now
+        adminClient.realm(DEMO).components().component(oldKeyId).remove();
 
         // Set some offset to ensure pushing notBefore will pass
         setAdapterAndServerTimeOffset(130, customerDb.toString() + "/unsecured/foo", tokenMinTTLPage.toString() + "/unsecured/foo");
 
-        // Send REST request to the REST app. Should be still succcessfully authenticated as the JWKPublicKeyLocator cache is still valid
-        status = invokeRESTEndpoint(accessTokenString);
-        Assert.assertEquals(200, status);
-
         // Send notBefore policy from the realm
         demoRealm.setNotBefore(Time.currentTime() - 1);
         adminClient.realm(DEMO).update(demoRealm);
@@ -281,9 +283,6 @@ public class AbstractOIDCPublicKeyRotationAdapterTest extends AbstractServletsAd
     private void generateNewRealmKey() {
         String realmId = adminClient.realm(DEMO).toRepresentation().getId();
 
-        String oldKeyId = adminClient.realm(DEMO).components().query(realmId, KeyProvider.class.getName())
-                .get(0).getId();
-
         ComponentRepresentation keys = new ComponentRepresentation();
         keys.setName("generated");
         keys.setProviderType(KeyProvider.class.getName());
@@ -294,9 +293,12 @@ public class AbstractOIDCPublicKeyRotationAdapterTest extends AbstractServletsAd
         Response response = adminClient.realm(DEMO).components().add(keys);
         assertEquals(201, response.getStatus());
         response.close();
+    }
 
-        // Remove original key
-        adminClient.realm(DEMO).components().component(oldKeyId).remove();
+    private String getActiveKeyId() {
+        String realmId = adminClient.realm(DEMO).toRepresentation().getId();
+        return adminClient.realm(DEMO).components().query(realmId, KeyProvider.class.getName())
+                .get(0).getId();
     }