keycloak-aplcache

[KEYCLOAK-9478] - Support multiple CIP providers in the policy

2/1/2019 4:25:44 PM

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
index 943f517..138e143 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
@@ -349,24 +349,23 @@ public abstract class AbstractPolicyEnforcer {
     }
 
     protected Map<String, List<String>> resolveClaims(PathConfig pathConfig, OIDCHttpFacade httpFacade) {
-        Map<String, List<String>> claims = getClaims(getEnforcerConfig().getClaimInformationPointConfig(), httpFacade);
+        Map<String, List<String>> claims = new HashMap<>();
 
-        claims.putAll(getClaims(pathConfig.getClaimInformationPointConfig(), httpFacade));
+        resolveClaims(claims, getEnforcerConfig().getClaimInformationPointConfig(), httpFacade);
+        resolveClaims(claims, pathConfig.getClaimInformationPointConfig(), httpFacade);
 
         return claims;
     }
 
-    private Map<String, List<String>> getClaims(Map<String, Map<String, Object>>claimInformationPointConfig, HttpFacade httpFacade) {
+    private void resolveClaims(Map<String, List<String>> claims, Map<String, Map<String, Object>> claimInformationPointConfig, HttpFacade httpFacade) {
         if (claimInformationPointConfig != null) {
             for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
                 ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
 
                 if (factory != null) {
-                    return factory.create(claimDef.getValue()).resolve(httpFacade);
+                    claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
                 }
             }
         }
-
-        return new HashMap<>();
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/MyCustomCIPFactory.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/MyCustomCIPFactory.java
new file mode 100644
index 0000000..163bee0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/MyCustomCIPFactory.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.admin.client.authorization;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.keycloak.adapters.authorization.ClaimInformationPointProvider;
+import org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory;
+import org.keycloak.adapters.authorization.PolicyEnforcer;
+import org.keycloak.adapters.spi.HttpFacade;
+
+public class MyCustomCIPFactory implements ClaimInformationPointProviderFactory<MyCustomCIP> {
+
+    @Override
+    public String getName() {
+        return "my-custom-cip";
+    }
+
+    @Override
+    public void init(PolicyEnforcer policyEnforcer) {
+
+    }
+
+    @Override
+    public MyCustomCIP create(Map<String, Object> config) {
+        return new MyCustomCIP(config);
+    }
+}
+
+class MyCustomCIP implements ClaimInformationPointProvider {
+
+    private final Map<String, Object> config;
+
+    MyCustomCIP(Map<String, Object> config) {
+        this.config = config;
+    }
+
+    @Override
+    public Map<String, List<String>> resolve(HttpFacade httpFacade) {
+        Map<String, List<String>> claims = new HashMap<>();
+
+        claims.put("resolved-claim", Arrays.asList(config.get("claim-value").toString()));
+
+        return claims;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/PolicyEnforcerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/PolicyEnforcerTest.java
index 160113d..a38db00 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/PolicyEnforcerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/PolicyEnforcerTest.java
@@ -34,6 +34,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -68,6 +69,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.authorization.AuthorizationRequest;
 import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
@@ -175,13 +177,40 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
             @Override
             public String apply(String s) {
                 Assert.assertTrue(resolved.compareAndSet(false, true));
-                return "claim-value";
+                return "value-" + s;
             }
         });
 
         AuthorizationContext context = policyEnforcer.enforce(httpFacade);
+        Permission permission = context.getPermissions().get(0);
+        Map<String, Set<String>> claims = permission.getClaims();
 
         assertTrue(context.isGranted());
+        assertEquals("value-claim-a", claims.get("claim-a").iterator().next());
+        assertEquals("claim-b", claims.get("claim-b").iterator().next());
+    }
+
+    @Test
+    public void testCustomClaimProvider() {
+        KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getAdapterConfiguration("enforcer-bearer-only-with-cip.json"));
+        PolicyEnforcer policyEnforcer = deployment.getPolicyEnforcer();
+
+        oauth.realm(REALM_NAME);
+        oauth.clientId("public-client-test");
+        oauth.doLogin("marta", "password");
+
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, null);
+        String token = response.getAccessToken();
+
+        OIDCHttpFacade httpFacade = createHttpFacade("/api/resourcea", token);
+
+        AuthorizationContext context = policyEnforcer.enforce(httpFacade);
+        Permission permission = context.getPermissions().get(0);
+        Map<String, Set<String>> claims = permission.getClaims();
+
+        assertTrue(context.isGranted());
+        assertEquals("test", claims.get("resolved-claim").iterator().next());
     }
 
     @Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/enforcer-bearer-only-with-cip.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/enforcer-bearer-only-with-cip.json
index 1b78f5f..8ca0253 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/enforcer-bearer-only-with-cip.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/enforcer-bearer-only-with-cip.json
@@ -19,6 +19,9 @@
         "claim-information-point": {
           "claims": {
             "claim-a": "{request.parameter['claim-a']}"
+          },
+          "my-custom-cip": {
+            "claim-value": "test"
           }
         }
       }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/services/org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/services/org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory
new file mode 100644
index 0000000..fa1de4b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/services/org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory
@@ -0,0 +1,18 @@
+#
+# * Copyright 2018 Red Hat, Inc. and/or its affiliates
+# * and other contributors as indicated by the @author tags.
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+#
+
+org.keycloak.testsuite.admin.client.authorization.MyCustomCIPFactory
\ No newline at end of file