keycloak-memoizeit
Changes
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java 15(+14 -1)
core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java 12(+12 -0)
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 add1c83..3ae286f 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
@@ -129,7 +129,7 @@ public abstract class AbstractPolicyEnforcer {
Set<String> allowedScopes = permission.getScopes();
if (permission.getResourceSetId() != null) {
- if (permission.getResourceSetId().equals(actualPathConfig.getId())) {
+ if (isResourcePermission(actualPathConfig, permission)) {
if (((allowedScopes == null || allowedScopes.isEmpty()) && requiredScopes.isEmpty()) || allowedScopes.containsAll(requiredScopes)) {
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
@@ -211,6 +211,7 @@ public abstract class AbstractPolicyEnforcer {
config.setScopes(originalConfig.getScopes());
config.setMethods(originalConfig.getMethods());
config.setInstance(true);
+ config.setParentConfig(originalConfig);
this.paths.add(config);
@@ -240,4 +241,16 @@ public abstract class AbstractPolicyEnforcer {
private AuthorizationContext createAuthorizationContext(AccessToken accessToken) {
return new AuthorizationContext(accessToken, this.paths);
}
+
+ private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
+ // first we try a match using resource id
+ boolean resourceMatch = permission.getResourceSetId().equals(actualPathConfig.getId());
+
+ // as a fallback, check if the current path is an instance and if so, check if parent's id matches the permission
+ if (!resourceMatch && actualPathConfig.isInstance()) {
+ resourceMatch = permission.getResourceSetId().equals(actualPathConfig.getParentConfig().getId());
+ }
+
+ return resourceMatch;
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
index 98a4045..5145ec7 100644
--- a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
@@ -17,6 +17,7 @@
*/
package org.keycloak.representations.adapters.config;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
@@ -99,6 +100,9 @@ public class PolicyEnforcerConfig {
private String id;
private boolean instance;
+ @JsonIgnore
+ private PathConfig parentConfig;
+
public String getPath() {
return this.path;
}
@@ -169,6 +173,14 @@ public class PolicyEnforcerConfig {
public void setInstance(boolean instance) {
this.instance = instance;
}
+
+ public void setParentConfig(PathConfig parentConfig) {
+ this.parentConfig = parentConfig;
+ }
+
+ public PathConfig getParentConfig() {
+ return parentConfig;
+ }
}
public static class MethodConfig {
diff --git a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
index eaa8c78..ad154a6 100644
--- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
+++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
@@ -106,7 +106,10 @@ public class AuthorizationTokenService {
List<Permission> entitlements = Permissions.allPermits(results);
if (entitlements.isEmpty()) {
- asyncResponse.resume(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN));
+ asyncResponse.resume(Cors.add(httpRequest, Response.status(Status.FORBIDDEN)
+ .entity(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN)))
+ .allowedOrigins(identity.getAccessToken())
+ .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
} else {
AuthorizationResponse response = new AuthorizationResponse(createRequestingPartyToken(entitlements, identity.getAccessToken()));
asyncResponse.resume(Cors.add(httpRequest, Response.status(Status.CREATED).entity(response)).allowedOrigins(identity.getAccessToken())
@@ -217,12 +220,14 @@ public class AuthorizationTokenService {
}
private PermissionTicket verifyPermissionTicket(AuthorizationRequest request) {
- if (!Tokens.verifySignature(request.getTicket(), getRealm().getPublicKey())) {
+ String ticketString = request.getTicket();
+
+ if (ticketString == null || !Tokens.verifySignature(ticketString, getRealm().getPublicKey())) {
throw new ErrorResponseException("invalid_ticket", "Ticket verification failed", Status.FORBIDDEN);
}
try {
- PermissionTicket ticket = new JWSInput(request.getTicket()).readJsonContent(PermissionTicket.class);
+ PermissionTicket ticket = new JWSInput(ticketString).readJsonContent(PermissionTicket.class);
if (!ticket.isActive()) {
throw new ErrorResponseException("invalid_ticket", "Invalid permission ticket.", Status.FORBIDDEN);
diff --git a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
index 983646b..df6f54d 100644
--- a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
+++ b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
@@ -80,8 +80,9 @@ public class EntitlementService {
this.authorization = authorization;
}
+ @Path("{resource_server_id}")
@OPTIONS
- public Response authorizePreFlight() {
+ public Response authorizePreFlight(@PathParam("resource_server_id") String resourceServerId) {
return Cors.add(this.request, Response.ok()).auth().preflight().build();
}
@@ -118,7 +119,10 @@ public class EntitlementService {
List<Permission> entitlements = Permissions.allPermits(results);
if (entitlements.isEmpty()) {
- asyncResponse.resume(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN));
+ asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN)
+ .entity(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN)))
+ .allowedOrigins(identity.getAccessToken())
+ .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
} else {
asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
}
diff --git a/services/src/main/java/org/keycloak/authorization/util/Permissions.java b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
index 240fafa..43204b8 100644
--- a/services/src/main/java/org/keycloak/authorization/util/Permissions.java
+++ b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
@@ -26,6 +26,7 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.evaluation.Result;
+import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.representations.authorization.Permission;
@@ -58,9 +59,10 @@ public final class Permissions {
public static List<ResourcePermission> all(ResourceServer resourceServer, Identity identity, AuthorizationProvider authorization) {
List<ResourcePermission> permissions = new ArrayList<>();
StoreFactory storeFactory = authorization.getStoreFactory();
+ ResourceStore resourceStore = storeFactory.getResourceStore();
- storeFactory.getResourceStore().findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource)));
- storeFactory.getResourceStore().findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource)));
+ resourceStore.findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource)));
+ resourceStore.findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource)));
return permissions;
}