[KEYCLOAK-3156] - Missing CORS when responding with denies

This commit is contained in:
Pedro Igor 2016-06-22 14:39:07 -03:00
parent 333bdb62dd
commit f48288865b
5 changed files with 44 additions and 8 deletions

View file

@ -129,7 +129,7 @@ public abstract class AbstractPolicyEnforcer {
Set<String> allowedScopes = permission.getScopes(); Set<String> allowedScopes = permission.getScopes();
if (permission.getResourceSetId() != null) { if (permission.getResourceSetId() != null) {
if (permission.getResourceSetId().equals(actualPathConfig.getId())) { if (isResourcePermission(actualPathConfig, permission)) {
if (((allowedScopes == null || allowedScopes.isEmpty()) && requiredScopes.isEmpty()) || allowedScopes.containsAll(requiredScopes)) { if (((allowedScopes == null || allowedScopes.isEmpty()) && requiredScopes.isEmpty()) || allowedScopes.containsAll(requiredScopes)) {
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions); LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) { if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
@ -211,6 +211,7 @@ public abstract class AbstractPolicyEnforcer {
config.setScopes(originalConfig.getScopes()); config.setScopes(originalConfig.getScopes());
config.setMethods(originalConfig.getMethods()); config.setMethods(originalConfig.getMethods());
config.setInstance(true); config.setInstance(true);
config.setParentConfig(originalConfig);
this.paths.add(config); this.paths.add(config);
@ -240,4 +241,16 @@ public abstract class AbstractPolicyEnforcer {
private AuthorizationContext createAuthorizationContext(AccessToken accessToken) { private AuthorizationContext createAuthorizationContext(AccessToken accessToken) {
return new AuthorizationContext(accessToken, this.paths); 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;
}
} }

View file

@ -17,6 +17,7 @@
*/ */
package org.keycloak.representations.adapters.config; package org.keycloak.representations.adapters.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList; import java.util.ArrayList;
@ -99,6 +100,9 @@ public class PolicyEnforcerConfig {
private String id; private String id;
private boolean instance; private boolean instance;
@JsonIgnore
private PathConfig parentConfig;
public String getPath() { public String getPath() {
return this.path; return this.path;
} }
@ -169,6 +173,14 @@ public class PolicyEnforcerConfig {
public void setInstance(boolean instance) { public void setInstance(boolean instance) {
this.instance = instance; this.instance = instance;
} }
public void setParentConfig(PathConfig parentConfig) {
this.parentConfig = parentConfig;
}
public PathConfig getParentConfig() {
return parentConfig;
}
} }
public static class MethodConfig { public static class MethodConfig {

View file

@ -106,7 +106,10 @@ public class AuthorizationTokenService {
List<Permission> entitlements = Permissions.allPermits(results); List<Permission> entitlements = Permissions.allPermits(results);
if (entitlements.isEmpty()) { 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 { } else {
AuthorizationResponse response = new AuthorizationResponse(createRequestingPartyToken(entitlements, identity.getAccessToken())); AuthorizationResponse response = new AuthorizationResponse(createRequestingPartyToken(entitlements, identity.getAccessToken()));
asyncResponse.resume(Cors.add(httpRequest, Response.status(Status.CREATED).entity(response)).allowedOrigins(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) { 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); throw new ErrorResponseException("invalid_ticket", "Ticket verification failed", Status.FORBIDDEN);
} }
try { try {
PermissionTicket ticket = new JWSInput(request.getTicket()).readJsonContent(PermissionTicket.class); PermissionTicket ticket = new JWSInput(ticketString).readJsonContent(PermissionTicket.class);
if (!ticket.isActive()) { if (!ticket.isActive()) {
throw new ErrorResponseException("invalid_ticket", "Invalid permission ticket.", Status.FORBIDDEN); throw new ErrorResponseException("invalid_ticket", "Invalid permission ticket.", Status.FORBIDDEN);

View file

@ -80,8 +80,9 @@ public class EntitlementService {
this.authorization = authorization; this.authorization = authorization;
} }
@Path("{resource_server_id}")
@OPTIONS @OPTIONS
public Response authorizePreFlight() { public Response authorizePreFlight(@PathParam("resource_server_id") String resourceServerId) {
return Cors.add(this.request, Response.ok()).auth().preflight().build(); return Cors.add(this.request, Response.ok()).auth().preflight().build();
} }
@ -118,7 +119,10 @@ public class EntitlementService {
List<Permission> entitlements = Permissions.allPermits(results); List<Permission> entitlements = Permissions.allPermits(results);
if (entitlements.isEmpty()) { 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 { } 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()); asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
} }

View file

@ -26,6 +26,7 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.permission.ResourcePermission; import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.evaluation.Result; import org.keycloak.authorization.policy.evaluation.Result;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.StoreFactory; import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.representations.authorization.Permission; 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) { public static List<ResourcePermission> all(ResourceServer resourceServer, Identity identity, AuthorizationProvider authorization) {
List<ResourcePermission> permissions = new ArrayList<>(); List<ResourcePermission> permissions = new ArrayList<>();
StoreFactory storeFactory = authorization.getStoreFactory(); StoreFactory storeFactory = authorization.getStoreFactory();
ResourceStore resourceStore = storeFactory.getResourceStore();
storeFactory.getResourceStore().findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource))); resourceStore.findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource)));
storeFactory.getResourceStore().findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource))); resourceStore.findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource)));
return permissions; return permissions;
} }