keycloak-uncached
Changes
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java 17(+7 -10)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java 12(+5 -7)
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 cf7e6d4..290582f 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
@@ -112,8 +112,9 @@ public abstract class AbstractPolicyEnforcer {
}
MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
+ Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
- if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
+ if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade, claims)) {
try {
return createAuthorizationContext(accessToken, pathConfig);
} catch (Exception e) {
@@ -142,7 +143,7 @@ public abstract class AbstractPolicyEnforcer {
protected abstract boolean challenge(PathConfig pathConfig, MethodConfig methodConfig, OIDCHttpFacade facade);
- protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
+ protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
Request request = httpFacade.getRequest();
if (isDefaultAccessDeniedUri(request)) {
@@ -175,7 +176,7 @@ public abstract class AbstractPolicyEnforcer {
policyEnforcer.getPathMatcher().removeFromCache(getPath(request));
}
- return hasValidClaims(actualPathConfig, permission, httpFacade, authorization);
+ return hasValidClaims(permission, claims);
}
}
} else {
@@ -197,12 +198,10 @@ public abstract class AbstractPolicyEnforcer {
return false;
}
- private boolean hasValidClaims(PathConfig actualPathConfig, Permission permission, OIDCHttpFacade httpFacade, Authorization authorization) {
+ private boolean hasValidClaims(Permission permission, Map<String, List<String>> claims) {
Map<String, Set<String>> grantedClaims = permission.getClaims();
if (grantedClaims != null) {
- Map<String, List<String>> claims = resolveClaims(actualPathConfig, httpFacade);
-
if (claims.isEmpty()) {
return false;
}
@@ -349,18 +348,16 @@ public abstract class AbstractPolicyEnforcer {
}
private Map<String, List<String>> getClaims(Map<String, Map<String, Object>>claimInformationPointConfig, HttpFacade httpFacade) {
- Map<String, List<String>> claims = new HashMap<>();
-
if (claimInformationPointConfig != null) {
for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
if (factory != null) {
- claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
+ return factory.create(claimDef.getValue()).resolve(httpFacade);
}
}
}
- return claims;
+ return new HashMap<>();
}
}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
index 07409f3..43b66db 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
@@ -55,14 +55,14 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
}
@Override
- protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
+ protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
AccessToken original = accessToken;
- if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
+ if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade, claims)) {
return true;
}
- accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade);
+ accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade, claims);
if (accessToken == null) {
return false;
@@ -90,7 +90,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
original.setAuthorization(authorization);
- return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade);
+ return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade, claims);
}
@Override
@@ -133,7 +133,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
}
}
- private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
+ private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
if (getEnforcerConfig().getUserManagedAccess() != null) {
return null;
}
@@ -149,8 +149,6 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
}
- Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
-
if (!claims.isEmpty()) {
authzRequest.setClaimTokenFormat("urn:ietf:params:oauth:token-type:jwt");
authzRequest.setClaimToken(Base64.encodeBytes(JsonSerialization.writeValueAsBytes(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 2c2ac88..49eeec9 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
@@ -33,8 +33,11 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
import java.util.stream.Collectors;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -154,6 +157,34 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
}
@Test
+ public void testResolvingClaimsOnce() {
+ 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, new Function<String, String>() {
+ AtomicBoolean resolved = new AtomicBoolean();
+
+ @Override
+ public String apply(String s) {
+ Assert.assertTrue(resolved.compareAndSet(false, true));
+ return "claim-value";
+ }
+ });
+
+ AuthorizationContext context = policyEnforcer.enforce(httpFacade);
+
+ assertTrue(context.isGranted());
+ }
+
+ @Test
public void testOnDenyRedirectTo() {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getAdapterConfiguration("enforcer-on-deny-redirect.json"));
PolicyEnforcer policyEnforcer = deployment.getPolicyEnforcer();
@@ -308,6 +339,10 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
}
private OIDCHttpFacade createHttpFacade(String path, String method, String token, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody, KeycloakDeployment deployment) {
+ return createHttpFacade(path, method, token, headers, parameters, requestBody, deployment, null);
+ }
+
+ private OIDCHttpFacade createHttpFacade(String path, String method, String token, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody, KeycloakDeployment deployment, Function<String, String> parameterFunction) {
return new OIDCHttpFacade() {
Request request;
Response response;
@@ -329,7 +364,7 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
@Override
public Request getRequest() {
if (request == null) {
- request = createHttpRequest(path, method, headers, parameters, requestBody);
+ request = createHttpRequest(path, method, headers, parameters, requestBody, parameterFunction);
}
return request;
}
@@ -357,11 +392,27 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
return createHttpFacade(path, null, null, new HashMap<>(), new HashMap<>(), null, null);
}
+ private OIDCHttpFacade createHttpFacade(String path, String token, Function<String, String> parameterFunction) {
+ return createHttpFacade(path, null, token, new HashMap<>(), new HashMap<>(), null, null, parameterFunction);
+ }
+
private Response createHttpResponse(Map<String, List<String>> headers) {
return new TestResponse(headers);
}
- private Request createHttpRequest(String path, String method, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody) {
+ private Request createHttpRequest(String path, String method, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody, Function<String, String> parameterFunction) {
+ if (parameterFunction == null) {
+ parameterFunction = param -> {
+ List<String> values = parameters.getOrDefault(param, Collections.emptyList());
+
+ if (!values.isEmpty()) {
+ return values.get(0);
+ }
+
+ return null;
+ };
+ }
+ Function<String, String> finalParameterFunction = parameterFunction;
return new Request() {
private InputStream inputStream;
@@ -388,13 +439,7 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
@Override
public String getFirstParam(String param) {
- List<String> values = parameters.getOrDefault(param, Collections.emptyList());
-
- if (!values.isEmpty()) {
- return values.get(0);
- }
-
- return null;
+ return finalParameterFunction.apply(param);
}
@Override
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
new file mode 100644
index 0000000..1b78f5f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/enforcer-bearer-only-with-cip.json
@@ -0,0 +1,27 @@
+{
+ "realm": "authz-test",
+ "auth-server-url": "http://localhost:8180/auth",
+ "ssl-required": "external",
+ "resource": "resource-server-test",
+ "credentials": {
+ "secret": "secret"
+ },
+ "bearer-only": true,
+ "policy-enforcer": {
+ "claim-information-point": {
+ "claims": {
+ "claim-b": "claim-b"
+ }
+ },
+ "paths": [
+ {
+ "path": "/api/resourcea",
+ "claim-information-point": {
+ "claims": {
+ "claim-a": "{request.parameter['claim-a']}"
+ }
+ }
+ }
+ ]
+ }
+}