keycloak-aplcache

Merge pull request #4689 from pedroigor/KEYCLOAK-5844 [KEYCLOAK-5844]

11/14/2017 6:25:24 PM

Details

diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java b/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
index 5ed1306..cb7389a 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
@@ -22,12 +22,15 @@ import org.keycloak.authorization.client.resource.AuthorizationResource;
 import org.keycloak.authorization.client.resource.EntitlementResource;
 import org.keycloak.authorization.client.resource.ProtectionResource;
 import org.keycloak.authorization.client.util.Http;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.util.JsonSerialization;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
+import java.util.function.Supplier;
 
 /**
  * <p>This is class serves as an entry point for clients looking for access to Keycloak Authorization Services.
@@ -37,6 +40,7 @@ import java.net.URI;
 public class AuthzClient {
 
     private final Http http;
+    private Supplier<String> patSupplier;
 
     public static AuthzClient create() {
         InputStream configStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("keycloak.json");
@@ -96,7 +100,7 @@ public class AuthzClient {
     }
 
     public ProtectionResource protection() {
-        return new ProtectionResource(this.http, obtainAccessToken().getToken());
+        return new ProtectionResource(this.http, createPatSupplier());
     }
 
     public AuthorizationResource authorization(String accesstoken) {
@@ -136,4 +140,40 @@ public class AuthzClient {
     public Configuration getConfiguration() {
         return this.deployment;
     }
+
+    private Supplier<String> createPatSupplier() {
+        if (patSupplier == null) {
+            patSupplier = new Supplier<String>() {
+                AccessTokenResponse clientToken = obtainAccessToken();
+
+                @Override
+                public String get() {
+                    String token = clientToken.getToken();
+
+                    try {
+                        AccessToken accessToken = JsonSerialization.readValue(new JWSInput(token).getContent(), AccessToken.class);
+
+                        if (accessToken.isActive()) {
+                            return token;
+                        }
+
+                        clientToken = http.<AccessTokenResponse>post(serverConfiguration.getTokenEndpoint())
+                                .authentication().client()
+                                .form()
+                                .param("grant_type", "refresh_token")
+                                .param("refresh_token", clientToken.getRefreshToken())
+                                .response()
+                                .json(AccessTokenResponse.class)
+                                .execute();
+                    } catch (Exception e) {
+                        patSupplier = null;
+                        throw new RuntimeException(e);
+                    }
+
+                    return clientToken.getToken();
+                }
+            };
+        }
+        return patSupplier;
+    }
 }
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java
index 960955f..5aaccda 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java
@@ -19,6 +19,8 @@ package org.keycloak.authorization.client.resource;
 
 import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException;
 
+import java.util.function.Supplier;
+
 import org.keycloak.authorization.client.representation.PermissionRequest;
 import org.keycloak.authorization.client.representation.PermissionResponse;
 import org.keycloak.authorization.client.util.Http;
@@ -30,9 +32,9 @@ import org.keycloak.util.JsonSerialization;
 public class PermissionResource {
 
     private final Http http;
-    private final String pat;
+    private final Supplier<String> pat;
 
-    public PermissionResource(Http http, String pat) {
+    public PermissionResource(Http http, Supplier<String> pat) {
         this.http = http;
         this.pat = pat;
     }
@@ -40,7 +42,7 @@ public class PermissionResource {
     public PermissionResponse forResource(PermissionRequest request) {
         try {
             return this.http.<PermissionResponse>post("/authz/protection/permission")
-                    .authorizationBearer(this.pat)
+                    .authorizationBearer(this.pat.get())
                     .json(JsonSerialization.writeValueAsBytes(request))
                     .response().json(PermissionResponse.class).execute();
         } catch (Exception cause) {
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
index d52ccc0..d774c3d 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
@@ -20,6 +20,7 @@ package org.keycloak.authorization.client.resource;
 import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException;
 
 import java.util.Set;
+import java.util.function.Supplier;
 
 import org.keycloak.authorization.client.representation.RegistrationResponse;
 import org.keycloak.authorization.client.representation.ResourceRepresentation;
@@ -32,9 +33,9 @@ import org.keycloak.util.JsonSerialization;
 public class ProtectedResource {
 
     private final Http http;
-    private final String pat;
+    private final Supplier<String> pat;
 
-    public ProtectedResource(Http http, String pat) {
+    public ProtectedResource(Http http, Supplier<String> pat) {
         this.http = http;
         this.pat = pat;
     }
@@ -42,7 +43,7 @@ public class ProtectedResource {
     public RegistrationResponse create(ResourceRepresentation resource) {
         try {
             return this.http.<RegistrationResponse>post("/authz/protection/resource_set")
-                    .authorizationBearer(this.pat)
+                    .authorizationBearer(this.pat.get())
                     .json(JsonSerialization.writeValueAsBytes(resource))
                     .response().json(RegistrationResponse.class).execute();
         } catch (Exception cause) {
@@ -53,7 +54,7 @@ public class ProtectedResource {
     public void update(ResourceRepresentation resource) {
         try {
             this.http.<RegistrationResponse>put("/authz/protection/resource_set/" + resource.getId())
-                    .authorizationBearer(this.pat)
+                    .authorizationBearer(this.pat.get())
                     .json(JsonSerialization.writeValueAsBytes(resource)).execute();
         } catch (Exception cause) {
             throw handleAndWrapException("Could not update resource", cause);
@@ -63,7 +64,7 @@ public class ProtectedResource {
     public RegistrationResponse findById(String id) {
         try {
             return this.http.<RegistrationResponse>get("/authz/protection/resource_set/" + id)
-                    .authorizationBearer(this.pat)
+                    .authorizationBearer(this.pat.get())
                     .response().json(RegistrationResponse.class).execute();
         } catch (Exception cause) {
             throw handleAndWrapException("Could not find resource", cause);
@@ -73,7 +74,7 @@ public class ProtectedResource {
     public Set<String> findByFilter(String filter) {
         try {
             return this.http.<Set>get("/authz/protection/resource_set")
-                    .authorizationBearer(this.pat)
+                    .authorizationBearer(this.pat.get())
                     .param("filter", filter)
                     .response().json(Set.class).execute();
         } catch (Exception cause) {
@@ -84,7 +85,7 @@ public class ProtectedResource {
     public Set<String> findAll() {
         try {
             return this.http.<Set>get("/authz/protection/resource_set")
-                    .authorizationBearer(this.pat)
+                    .authorizationBearer(this.pat.get())
                     .response().json(Set.class).execute();
         } catch (Exception cause) {
             throw handleAndWrapException("Could not find resource", cause);
@@ -94,7 +95,7 @@ public class ProtectedResource {
     public void delete(String id) {
         try {
             this.http.delete("/authz/protection/resource_set/" + id)
-                    .authorizationBearer(this.pat)
+                    .authorizationBearer(this.pat.get())
                     .execute();
         } catch (Exception cause) {
             throw handleAndWrapException("Could not delete resource", cause);
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
index fc24f0e..fd3bee9 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
@@ -17,6 +17,8 @@
  */
 package org.keycloak.authorization.client.resource;
 
+import java.util.function.Supplier;
+
 import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
 import org.keycloak.authorization.client.util.Http;
 
@@ -25,10 +27,10 @@ import org.keycloak.authorization.client.util.Http;
  */
 public class ProtectionResource {
 
-    private final String pat;
+    private final Supplier<String> pat;
     private final Http http;
 
-    public ProtectionResource(Http http, String pat) {
+    public ProtectionResource(Http http, Supplier<String> pat) {
         if (pat == null) {
             throw new RuntimeException("No access token was provided when creating client for Protection API.");
         }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
index 38a194d..cfeb153 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
@@ -52,6 +52,7 @@ import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserSessionRepresentation;
 import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
 import org.keycloak.testsuite.AbstractKeycloakTest;
@@ -73,6 +74,7 @@ public class AuthzClientCredentialsTest extends AbstractAuthzTest {
                 .authenticatorType(JWTClientAuthenticator.PROVIDER_ID))
                 .build());
         testRealms.add(configureRealm(RealmBuilder.create().name("authz-test"), ClientBuilder.create().secret("secret")).build());
+        testRealms.add(configureRealm(RealmBuilder.create().name("authz-test-session").accessTokenLifespan(1), ClientBuilder.create().secret("secret")).build());
     }
 
     @Before
@@ -168,6 +170,31 @@ public class AuthzClientCredentialsTest extends AbstractAuthzTest {
         assertAccessProtectionAPI(protection);
     }
 
+    @Test
+    public void testReusingAccessAndRefreshTokens() throws Exception {
+        ClientsResource clients = getAdminClient().realm("authz-test-session").clients();
+        ClientRepresentation clientRepresentation = clients.findByClientId("resource-server-test").get(0);
+        List<UserSessionRepresentation> userSessions = clients.get(clientRepresentation.getId()).getUserSessions(-1, -1);
+
+        assertEquals(0, userSessions.size());
+
+        AuthzClient authzClient = getAuthzClient("default-session-keycloak.json");
+        ProtectionResource protection = authzClient.protection();
+
+        protection.resource().findByFilter("name=Default Resource");
+        userSessions = clients.get(clientRepresentation.getId()).getUserSessions(null, null);
+
+        assertEquals(1, userSessions.size());
+
+        Thread.sleep(2000);
+        protection = authzClient.protection();
+        protection.resource().findByFilter("name=Default Resource");
+
+        userSessions = clients.get(clientRepresentation.getId()).getUserSessions(null, null);
+
+        assertEquals(1, userSessions.size());
+    }
+
     private RealmBuilder configureRealm(RealmBuilder builder, ClientBuilder clientBuilder) {
         return builder
                 .roles(RolesBuilder.create().realmRole(new RoleRepresentation("uma_authorization", "", false)))
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-session-keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-session-keycloak.json
new file mode 100644
index 0000000..3c53b77
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-session-keycloak.json
@@ -0,0 +1,8 @@
+{
+    "realm": "authz-test-session",
+    "auth-server-url" : "http://localhost:8180/auth",
+    "resource" : "resource-server-test",
+    "credentials": {
+        "secret": "secret"
+    }
+}
\ No newline at end of file