[KEYCLOAK-9478] - Support multiple CIP providers in the policy enforcer configuration
This commit is contained in:
parent
bacc1b538f
commit
75d9847672
5 changed files with 119 additions and 7 deletions
|
@ -349,24 +349,23 @@ public abstract class AbstractPolicyEnforcer {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, List<String>> resolveClaims(PathConfig pathConfig, OIDCHttpFacade httpFacade) {
|
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;
|
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) {
|
if (claimInformationPointConfig != null) {
|
||||||
for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
|
for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
|
||||||
ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
|
ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
|
||||||
|
|
||||||
if (factory != null) {
|
if (factory != null) {
|
||||||
return factory.create(claimDef.getValue()).resolve(httpFacade);
|
claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HashMap<>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
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.AuthorizationRequest;
|
||||||
import org.keycloak.representations.idm.authorization.AuthorizationResponse;
|
import org.keycloak.representations.idm.authorization.AuthorizationResponse;
|
||||||
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
|
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.ResourcePermissionRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
|
||||||
|
@ -175,13 +177,40 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
||||||
@Override
|
@Override
|
||||||
public String apply(String s) {
|
public String apply(String s) {
|
||||||
Assert.assertTrue(resolved.compareAndSet(false, true));
|
Assert.assertTrue(resolved.compareAndSet(false, true));
|
||||||
return "claim-value";
|
return "value-" + s;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AuthorizationContext context = policyEnforcer.enforce(httpFacade);
|
AuthorizationContext context = policyEnforcer.enforce(httpFacade);
|
||||||
|
Permission permission = context.getPermissions().get(0);
|
||||||
|
Map<String, Set<String>> claims = permission.getClaims();
|
||||||
|
|
||||||
assertTrue(context.isGranted());
|
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
|
@Test
|
||||||
|
|
|
@ -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
|
|
@ -19,6 +19,9 @@
|
||||||
"claim-information-point": {
|
"claim-information-point": {
|
||||||
"claims": {
|
"claims": {
|
||||||
"claim-a": "{request.parameter['claim-a']}"
|
"claim-a": "{request.parameter['claim-a']}"
|
||||||
|
},
|
||||||
|
"my-custom-cip": {
|
||||||
|
"claim-value": "test"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue