keycloak-uncached

[KEYCLOAK-7754] - Fixing compat issues with UMA spec in RPT

8/28/2018 5:11:09 PM

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/Permission.java b/core/src/main/java/org/keycloak/representations/idm/authorization/Permission.java
index 4635613..56d7ec1 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/Permission.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/Permission.java
@@ -21,12 +21,14 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
+@JsonIgnoreProperties(ignoreUnknown = true)
 public class Permission {
 
     @JsonProperty("rsid")
diff --git a/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java b/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java
index 989dbe1..d8100ec 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java
@@ -17,17 +17,22 @@
  */
 package org.keycloak.authorization.protection.introspect;
 
-import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.jboss.logging.Logger;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.protocol.oidc.AccessTokenIntrospectionProvider;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessToken.Authorization;
+import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.util.JsonSerialization;
 
 /**
@@ -65,7 +70,19 @@ public class RPTIntrospectionProvider extends AccessTokenIntrospectionProvider {
                 metadata.setResourceAccess(null);
 
                 tokenMetadata = JsonSerialization.createObjectNode(metadata);
-                tokenMetadata.putPOJO("permissions", accessToken.getAuthorization().getPermissions());
+                Authorization authorization = accessToken.getAuthorization();
+
+                if (authorization != null) {
+                    Collection permissions;
+
+                    if (authorization.getPermissions() != null) {
+                        permissions = authorization.getPermissions().stream().map(UmaPermissionRepresentation::new).collect(Collectors.toSet());
+                    } else {
+                        permissions = Collections.emptyList();
+                    }
+
+                    tokenMetadata.putPOJO("permissions", permissions);
+                }
             } else {
                 tokenMetadata = JsonSerialization.createObjectNode();
             }
@@ -82,4 +99,26 @@ public class RPTIntrospectionProvider extends AccessTokenIntrospectionProvider {
     public void close() {
 
     }
+
+    //todo: we need to avoid creating this class when processing responses. The only reason for that is that
+    // UMA defines "resource_id" and "resource_scopes" claims but we use "rsid" and "scopes".
+    // To avoid breaking backward compatiblity we are just responding with all these claims.
+    public class UmaPermissionRepresentation extends Permission {
+
+        public UmaPermissionRepresentation(Permission permission) {
+            setResourceId(permission.getResourceId());
+            setResourceName(permission.getResourceName());
+            setScopes(permission.getScopes());
+        }
+
+        @JsonProperty("resource_id")
+        public String getUmaResourceId() {
+            return getResourceId();
+        }
+
+        @JsonProperty("resource_scopes")
+        public Set<String> getUmaResourceScopes() {
+            return getScopes();
+        }
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java
index 0d6203b..0aa9d6d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java
@@ -16,15 +16,19 @@
  */
 package org.keycloak.testsuite.authz;
 
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.keycloak.testsuite.util.OAuthClient.AUTH_SERVER_ROOT;
 
 import java.net.URI;
 import java.util.Collection;
-import java.util.List;
+import java.util.Map;
 
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
@@ -41,6 +45,8 @@ import org.keycloak.OAuth2Constants;
 import org.keycloak.admin.client.resource.AuthorizationResource;
 import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.authorization.client.AuthorizationDeniedException;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
 import org.keycloak.representations.AccessToken;
@@ -54,6 +60,7 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
 import org.keycloak.testsuite.util.OAuthClient;
 import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.JsonSerialization;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -456,6 +463,57 @@ public class UmaGrantTypeTest extends AbstractResourceServerTest {
         assertTrue(permissions.isEmpty());
     }
 
+    @Test
+    public void testTokenIntrospect() throws Exception {
+        AuthzClient authzClient = getAuthzClient();
+        AccessTokenResponse accessTokenResponse = authzClient.obtainAccessToken("marta", "password");
+        AuthorizationResponse response = authorize(null, null, null, null, accessTokenResponse.getToken(), null, null, new PermissionRequest("Resource A", "ScopeA", "ScopeB"));
+        String rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        AccessToken accessToken = toAccessToken(rpt);
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        Collection<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+
+        TokenIntrospectionResponse introspectionResponse = authzClient.protection().introspectRequestingPartyToken(rpt);
+
+        assertNotNull(introspectionResponse);
+        assertNotNull(introspectionResponse.getPermissions());
+
+        oauth.realm("authz-test");
+        String introspectHttpResponse = oauth.introspectTokenWithClientCredential("resource-server-test", "secret", "requesting_party_token", rpt);
+
+        Map jsonNode = JsonSerialization.readValue(introspectHttpResponse, Map.class);
+
+        assertEquals(true, jsonNode.get("active"));
+
+        Collection permissionClaims = (Collection) jsonNode.get("permissions");
+
+        assertNotNull(permissionClaims);
+        assertEquals(1, permissionClaims.size());
+
+        Map<String, Object> claim = (Map) permissionClaims.iterator().next();
+
+        assertThat(claim.keySet(), containsInAnyOrder("resource_id", "rsname", "resource_scopes", "scopes", "rsid"));
+        assertThat(claim.get("rsname"), equalTo("Resource A"));
+
+        ResourceRepresentation resourceRep = authzClient.protection().resource().findByName("Resource A");
+        assertThat(claim.get("rsid"), equalTo(resourceRep.getId()));
+        assertThat(claim.get("resource_id"), equalTo(resourceRep.getId()));
+
+        assertThat((Collection<String>) claim.get("resource_scopes"), containsInAnyOrder("ScopeA", "ScopeB"));
+        assertThat((Collection<String>) claim.get("scopes"), containsInAnyOrder("ScopeA", "ScopeB"));
+    }
+
     private String getIdToken(String username, String password) {
         oauth.realm("authz-test");
         oauth.clientId("test-app");