[KEYCLOAK-6787] - Wrong validation of resources with same name and different owners

This commit is contained in:
pedroigor 2018-03-01 16:50:05 -03:00
parent 9b1275f182
commit 1e1de85685
30 changed files with 535 additions and 217 deletions

View file

@ -97,7 +97,7 @@ public class AuthzClient {
* @return a {@link ProtectionResource} * @return a {@link ProtectionResource}
*/ */
public ProtectionResource protection() { public ProtectionResource protection() {
return new ProtectionResource(this.http, this.serverConfiguration, createPatSupplier()); return new ProtectionResource(this.http, this.serverConfiguration, configuration, createPatSupplier());
} }
/** /**
@ -107,7 +107,7 @@ public class AuthzClient {
* @return a {@link ProtectionResource} * @return a {@link ProtectionResource}
*/ */
public ProtectionResource protection(final String accessToken) { public ProtectionResource protection(final String accessToken) {
return new ProtectionResource(this.http, this.serverConfiguration, new TokenCallable(http, configuration, serverConfiguration) { return new ProtectionResource(this.http, this.serverConfiguration, configuration, new TokenCallable(http, configuration, serverConfiguration) {
@Override @Override
public String call() { public String call() {
return accessToken; return accessToken;
@ -128,7 +128,7 @@ public class AuthzClient {
* @return a {@link ProtectionResource} * @return a {@link ProtectionResource}
*/ */
public ProtectionResource protection(String userName, String password) { public ProtectionResource protection(String userName, String password) {
return new ProtectionResource(this.http, this.serverConfiguration, createPatSupplier(userName, password)); return new ProtectionResource(this.http, this.serverConfiguration, configuration, createPatSupplier(userName, password));
} }
/** /**

View file

@ -22,6 +22,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.authorization.client.representation.ResourceRepresentation; import org.keycloak.authorization.client.representation.ResourceRepresentation;
import org.keycloak.authorization.client.representation.ServerConfiguration; import org.keycloak.authorization.client.representation.ServerConfiguration;
import org.keycloak.authorization.client.util.Http; import org.keycloak.authorization.client.util.Http;
@ -38,11 +39,13 @@ public class ProtectedResource {
private final Http http; private final Http http;
private ServerConfiguration serverConfiguration; private ServerConfiguration serverConfiguration;
private final Configuration configuration;
private final TokenCallable pat; private final TokenCallable pat;
ProtectedResource(Http http, ServerConfiguration serverConfiguration, TokenCallable pat) { ProtectedResource(Http http, ServerConfiguration serverConfiguration, Configuration configuration, TokenCallable pat) {
this.http = http; this.http = http;
this.serverConfiguration = serverConfiguration; this.serverConfiguration = serverConfiguration;
this.configuration = configuration;
this.pat = pat; this.pat = pat;
} }
@ -119,13 +122,30 @@ public class ProtectedResource {
} }
/** /**
* Query the server for a resource given its <code>name</code>. * Query the server for a resource given its <code>name</code> where the owner is the resource server itself.
* *
* @param id the resource name * @param id the resource name
* @return a {@link ResourceRepresentation} * @return a {@link ResourceRepresentation}
*/ */
public ResourceRepresentation findByName(String name) { public ResourceRepresentation findByName(String name) {
String[] representations = find(null, name, null, null, null, null, null, null); String[] representations = find(null, name, null, configuration.getResource(), null, null, null, null);
if (representations.length == 0) {
return null;
}
return findById(representations[0]);
}
/**
* Query the server for a resource given its <code>name</code> and a given <code>ownerId</code>.
*
* @param name the resource name
* @param ownerId the owner id
* @return a {@link ResourceRepresentation}
*/
public ResourceRepresentation findByName(String name, String ownerId) {
String[] representations = find(null, name, null, ownerId, null, null, null, null);
if (representations.length == 0) { if (representations.length == 0) {
return null; return null;

View file

@ -17,6 +17,7 @@
*/ */
package org.keycloak.authorization.client.resource; package org.keycloak.authorization.client.resource;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.authorization.client.representation.ServerConfiguration; import org.keycloak.authorization.client.representation.ServerConfiguration;
import org.keycloak.authorization.client.representation.TokenIntrospectionResponse; import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
import org.keycloak.authorization.client.util.Http; import org.keycloak.authorization.client.util.Http;
@ -31,15 +32,17 @@ public class ProtectionResource {
private final TokenCallable pat; private final TokenCallable pat;
private final Http http; private final Http http;
private final Configuration configuration;
private ServerConfiguration serverConfiguration; private ServerConfiguration serverConfiguration;
public ProtectionResource(Http http, ServerConfiguration serverConfiguration, TokenCallable pat) { public ProtectionResource(Http http, ServerConfiguration serverConfiguration, Configuration configuration, TokenCallable pat) {
if (pat == null) { if (pat == null) {
throw new RuntimeException("No access token was provided when creating client for Protection API."); throw new RuntimeException("No access token was provided when creating client for Protection API.");
} }
this.http = http; this.http = http;
this.serverConfiguration = serverConfiguration; this.serverConfiguration = serverConfiguration;
this.configuration = configuration;
this.pat = pat; this.pat = pat;
} }
@ -49,7 +52,7 @@ public class ProtectionResource {
* @return a {@link ProtectedResource} * @return a {@link ProtectedResource}
*/ */
public ProtectedResource resource() { public ProtectedResource resource() {
return new ProtectedResource(http, serverConfiguration, pat); return new ProtectedResource(http, serverConfiguration, configuration, pat);
} }
/** /**

View file

@ -44,4 +44,12 @@ public class HttpResponseException extends RuntimeException {
public byte[] getBytes() { public byte[] getBytes() {
return bytes; return bytes;
} }
@Override
public String toString() {
if (bytes != null) {
return new StringBuilder(super.toString()).append(" / Response from server: ").append(new String(bytes)).toString();
}
return super.toString();
}
} }

View file

@ -39,7 +39,7 @@ public final class Throwables {
*/ */
public static RuntimeException handleWrapException(String message, Throwable cause) { public static RuntimeException handleWrapException(String message, Throwable cause) {
if (cause instanceof HttpResponseException) { if (cause instanceof HttpResponseException) {
throw handleAndWrapHttpResponseException(message, HttpResponseException.class.cast(cause)); throw handleAndWrapHttpResponseException(HttpResponseException.class.cast(cause));
} }
return new RuntimeException(message, cause); return new RuntimeException(message, cause);
@ -91,19 +91,11 @@ public final class Throwables {
throw new RuntimeException(message, cause); throw new RuntimeException(message, cause);
} }
private static RuntimeException handleAndWrapHttpResponseException(String message, HttpResponseException exception) { private static RuntimeException handleAndWrapHttpResponseException(HttpResponseException exception) {
HttpResponseException hre = HttpResponseException.class.cast(exception);
StringBuilder detail = new StringBuilder(message);
byte[] bytes = hre.getBytes();
if (bytes != null) {
detail.append(". Server message: ").append(new String(bytes));
}
if (403 == exception.getStatusCode()) { if (403 == exception.getStatusCode()) {
throw new AuthorizationDeniedException(detail.toString(), exception); throw new AuthorizationDeniedException(exception);
} }
return new RuntimeException(detail.toString(), exception); return new RuntimeException(exception);
} }
} }

View file

@ -59,6 +59,11 @@ public interface ResourcesResource {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
List<ResourceRepresentation> findByName(@QueryParam("name") String name); List<ResourceRepresentation> findByName(@QueryParam("name") String name);
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ResourceRepresentation> findByName(@QueryParam("name") String name, @QueryParam("owner") String owner);
@GET @GET
@NoCache @NoCache
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)

View file

@ -79,7 +79,7 @@ public class StoreFactoryCacheManager extends CacheManager {
public void resourceUpdated(String id, String name, String type, String uri, Set<String> scopes, String serverId, String owner, Set<String> invalidations) { public void resourceUpdated(String id, String name, String type, String uri, Set<String> scopes, String serverId, String owner, Set<String> invalidations) {
invalidations.add(id); invalidations.add(id);
invalidations.add(StoreFactoryCacheSession.getResourceByNameCacheKey(name, serverId)); invalidations.add(StoreFactoryCacheSession.getResourceByNameCacheKey(name, owner, serverId));
invalidations.add(StoreFactoryCacheSession.getResourceByOwnerCacheKey(owner, serverId)); invalidations.add(StoreFactoryCacheSession.getResourceByOwnerCacheKey(owner, serverId));
invalidations.add(StoreFactoryCacheSession.getResourceByOwnerCacheKey(owner, null)); invalidations.add(StoreFactoryCacheSession.getResourceByOwnerCacheKey(owner, null));
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByResource(id, serverId)); invalidations.add(StoreFactoryCacheSession.getPermissionTicketByResource(id, serverId));

View file

@ -336,8 +336,8 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
return "scope.name." + name + "." + serverId; return "scope.name." + name + "." + serverId;
} }
public static String getResourceByNameCacheKey(String name, String serverId) { public static String getResourceByNameCacheKey(String name, String ownerId, String serverId) {
return "resource.name." + name + "." + serverId; return "resource.name." + name + "." + ownerId + "." + serverId;
} }
public static String getResourceByOwnerCacheKey(String owner, String serverId) { public static String getResourceByOwnerCacheKey(String owner, String serverId) {
@ -580,10 +580,15 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
@Override @Override
public Resource findByName(String name, String resourceServerId) { public Resource findByName(String name, String resourceServerId) {
return findByName(name, resourceServerId, resourceServerId);
}
@Override
public Resource findByName(String name, String ownerId, String resourceServerId) {
if (name == null) return null; if (name == null) return null;
String cacheKey = getResourceByNameCacheKey(name, resourceServerId); String cacheKey = getResourceByNameCacheKey(name, ownerId, resourceServerId);
List<Resource> result = cacheQuery(cacheKey, ResourceListQuery.class, () -> { List<Resource> result = cacheQuery(cacheKey, ResourceListQuery.class, () -> {
Resource resource = getResourceStoreDelegate().findByName(name, resourceServerId); Resource resource = getResourceStoreDelegate().findByName(name, ownerId, resourceServerId);
if (resource == null) { if (resource == null) {
return Collections.emptyList(); return Collections.emptyList();

View file

@ -47,7 +47,7 @@ import java.util.List;
@NamedQuery(name="findResourceIdByOwner", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.owner = :owner"), @NamedQuery(name="findResourceIdByOwner", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.owner = :owner"),
@NamedQuery(name="findAnyResourceIdByOwner", query="select r.id from ResourceEntity r where r.owner = :owner"), @NamedQuery(name="findAnyResourceIdByOwner", query="select r.id from ResourceEntity r where r.owner = :owner"),
@NamedQuery(name="findResourceIdByUri", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.uri = :uri"), @NamedQuery(name="findResourceIdByUri", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.uri = :uri"),
@NamedQuery(name="findResourceIdByName", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.name = :name"), @NamedQuery(name="findResourceIdByName", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.owner = :ownerId and r.name = :name"),
@NamedQuery(name="findResourceIdByType", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.type = :type"), @NamedQuery(name="findResourceIdByType", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.type = :type"),
@NamedQuery(name="findResourceIdByServerId", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId "), @NamedQuery(name="findResourceIdByServerId", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId "),
@NamedQuery(name="findResourceIdByScope", query="select r.id from ResourceEntity r inner join r.scopes s where r.resourceServer.id = :serverId and (s.resourceServer.id = :serverId and s.id in (:scopeIds))"), @NamedQuery(name="findResourceIdByScope", query="select r.id from ResourceEntity r inner join r.scopes s where r.resourceServer.id = :serverId and (s.resourceServer.id = :serverId and s.id in (:scopeIds))"),

View file

@ -237,11 +237,17 @@ public class JPAResourceStore implements ResourceStore {
@Override @Override
public Resource findByName(String name, String resourceServerId) { public Resource findByName(String name, String resourceServerId) {
return findByName(name, resourceServerId, resourceServerId);
}
@Override
public Resource findByName(String name, String ownerId, String resourceServerId) {
TypedQuery<String> query = entityManager.createNamedQuery("findResourceIdByName", String.class); TypedQuery<String> query = entityManager.createNamedQuery("findResourceIdByName", String.class);
query.setFlushMode(FlushModeType.COMMIT); query.setFlushMode(FlushModeType.COMMIT);
query.setParameter("serverId", resourceServerId); query.setParameter("serverId", resourceServerId);
query.setParameter("name", name); query.setParameter("name", name);
query.setParameter("ownerId", ownerId);
try { try {
String id = query.getSingleResult(); String id = query.getSingleResult();

View file

@ -285,7 +285,7 @@ public final class AuthorizationProvider implements Provider {
} }
if (resource == null) { if (resource == null) {
throw new RuntimeException("Resource [" + id + "] does not exist"); throw new RuntimeException("Resource [" + id + "] does not exist or is not owned by the resource server.");
} }
return resource.getId(); return resource.getId();
@ -459,6 +459,11 @@ public final class AuthorizationProvider implements Provider {
return delegate.findByName(name, resourceServerId); return delegate.findByName(name, resourceServerId);
} }
@Override
public Resource findByName(String name, String ownerId, String resourceServerId) {
return delegate.findByName(name, ownerId, resourceServerId);
}
@Override @Override
public List<Resource> findByType(String type, String resourceServerId) { public List<Resource> findByType(String type, String resourceServerId) {
return delegate.findByType(type, resourceServerId); return delegate.findByType(type, resourceServerId);

View file

@ -117,7 +117,7 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
Resource resource = resourceStore.findById(permission.getResourceId(), resourceServer.getId()); Resource resource = resourceStore.findById(permission.getResourceId(), resourceServer.getId());
if (resource == null) { if (resource == null) {
resource = resourceStore.findByName(permission.getResourceId(), resourceServer.getId()); resource = resourceStore.findByName(permission.getResourceId(), identity.getId(), resourceServer.getId());
} }
if (!resource.isOwnerManagedAccess() || resource.getOwner().equals(identity.getId()) || resource.getOwner().equals(resourceServer.getId())) { if (!resource.isOwnerManagedAccess() || resource.getOwner().equals(identity.getId()) || resource.getOwner().equals(resourceServer.getId())) {

View file

@ -97,7 +97,7 @@ public interface ResourceStore {
List<Resource> findByScope(List<String> id, String resourceServerId); List<Resource> findByScope(List<String> id, String resourceServerId);
/** /**
* Find a {@link Resource} by its name. * Find a {@link Resource} by its name where the owner is the resource server itself.
* *
* @param name the name of the resource * @param name the name of the resource
* @param resourceServerId the identifier of the resource server * @param resourceServerId the identifier of the resource server
@ -105,6 +105,16 @@ public interface ResourceStore {
*/ */
Resource findByName(String name, String resourceServerId); Resource findByName(String name, String resourceServerId);
/**
* Find a {@link Resource} by its name where the owner is the given <code>ownerId</code>.
*
* @param name the name of the resource
* @param ownerId the owner id
* @param resourceServerId the identifier of the resource server
* @return a resource with the given name
*/
Resource findByName(String name, String ownerId, String resourceServerId);
/** /**
* Finds all {@link Resource} with the given type. * Finds all {@link Resource} with the given type.
* *

View file

@ -2275,7 +2275,7 @@ public class RepresentationToModel {
if (resource == null) { if (resource == null) {
resource = storeFactory.getResourceStore().findByName(resourceId, policy.getResourceServer().getId()); resource = storeFactory.getResourceStore().findByName(resourceId, policy.getResourceServer().getId());
if (resource == null) { if (resource == null) {
throw new RuntimeException("Resource with id or name [" + resourceId + "] does not exist"); throw new RuntimeException("Resource with id or name [" + resourceId + "] does not exist or is not owned by the resource server");
} }
} }
@ -2303,28 +2303,6 @@ public class RepresentationToModel {
public static Resource toModel(ResourceRepresentation resource, ResourceServer resourceServer, AuthorizationProvider authorization) { public static Resource toModel(ResourceRepresentation resource, ResourceServer resourceServer, AuthorizationProvider authorization) {
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore(); ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
Resource existing;
if (resource.getId() != null) {
existing = resourceStore.findById(resource.getId(), resourceServer.getId());
} else {
existing = resourceStore.findByName(resource.getName(), resourceServer.getId());
}
if (existing != null) {
existing.setName(resource.getName());
existing.setDisplayName(resource.getDisplayName());
existing.setType(resource.getType());
existing.setUri(resource.getUri());
existing.setIconUri(resource.getIconUri());
existing.setOwnerManagedAccess(Boolean.TRUE.equals(resource.getOwnerManagedAccess()));
existing.updateScopes(resource.getScopes().stream()
.map((ScopeRepresentation scope) -> toModel(scope, resourceServer, authorization))
.collect(Collectors.toSet()));
return existing;
}
ResourceOwnerRepresentation owner = resource.getOwner(); ResourceOwnerRepresentation owner = resource.getOwner();
if (owner == null) { if (owner == null) {
@ -2338,12 +2316,6 @@ public class RepresentationToModel {
throw new RuntimeException("No owner specified for resource [" + resource.getName() + "]."); throw new RuntimeException("No owner specified for resource [" + resource.getName() + "].");
} }
ClientModel clientModel = authorization.getRealm().getClientById(resourceServer.getId());
if (ownerId.equals(clientModel.getClientId())) {
ownerId = resourceServer.getId();
}
if (!resourceServer.getId().equals(ownerId)) { if (!resourceServer.getId().equals(ownerId)) {
RealmModel realm = authorization.getRealm(); RealmModel realm = authorization.getRealm();
KeycloakSession keycloakSession = authorization.getKeycloakSession(); KeycloakSession keycloakSession = authorization.getKeycloakSession();
@ -2361,6 +2333,28 @@ public class RepresentationToModel {
ownerId = ownerModel.getId(); ownerId = ownerModel.getId();
} }
Resource existing;
if (resource.getId() != null) {
existing = resourceStore.findById(resource.getId(), resourceServer.getId());
} else {
existing = resourceStore.findByName(resource.getName(), ownerId, resourceServer.getId());
}
if (existing != null) {
existing.setName(resource.getName());
existing.setDisplayName(resource.getDisplayName());
existing.setType(resource.getType());
existing.setUri(resource.getUri());
existing.setIconUri(resource.getIconUri());
existing.setOwnerManagedAccess(Boolean.TRUE.equals(resource.getOwnerManagedAccess()));
existing.updateScopes(resource.getScopes().stream()
.map((ScopeRepresentation scope) -> toModel(scope, resourceServer, authorization))
.collect(Collectors.toSet()));
return existing;
}
Resource model = resourceStore.create(resource.getName(), resourceServer, ownerId); Resource model = resourceStore.create(resource.getName(), resourceServer, ownerId);
model.setDisplayName(resource.getDisplayName()); model.setDisplayName(resource.getDisplayName());

View file

@ -118,7 +118,6 @@ public class ResourceSetService {
public Response create(ResourceRepresentation resource, Function<Resource, ?> toRepresentation) { public Response create(ResourceRepresentation resource, Function<Resource, ?> toRepresentation) {
requireManage(); requireManage();
StoreFactory storeFactory = this.authorization.getStoreFactory(); StoreFactory storeFactory = this.authorization.getStoreFactory();
Resource existingResource = storeFactory.getResourceStore().findByName(resource.getName(), this.resourceServer.getId());
ResourceOwnerRepresentation owner = resource.getOwner(); ResourceOwnerRepresentation owner = resource.getOwner();
if (owner == null) { if (owner == null) {
@ -132,7 +131,9 @@ public class ResourceSetService {
return ErrorResponse.error("You must specify the resource owner.", Status.BAD_REQUEST); return ErrorResponse.error("You must specify the resource owner.", Status.BAD_REQUEST);
} }
if (existingResource != null && existingResource.getOwner().equals(ownerId)) { Resource existingResource = storeFactory.getResourceStore().findByName(resource.getName(), ownerId, this.resourceServer.getId());
if (existingResource != null) {
return ErrorResponse.exists("Resource with name [" + resource.getName() + "] already exists."); return ErrorResponse.exists("Resource with name [" + resource.getName() + "] already exists.");
} }

View file

@ -172,13 +172,13 @@ public class AuthorizationTokenService {
private List<Result> evaluatePermissions(AuthorizationRequest authorizationRequest, PermissionTicketToken ticket, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) { private List<Result> evaluatePermissions(AuthorizationRequest authorizationRequest, PermissionTicketToken ticket, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
return authorization.evaluators() return authorization.evaluators()
.from(createPermissions(ticket, authorizationRequest, resourceServer, authorization), evaluationContext) .from(createPermissions(ticket, authorizationRequest, resourceServer, identity, authorization), evaluationContext)
.evaluate(); .evaluate();
} }
private List<Result> evaluateUserManagedPermissions(AuthorizationRequest request, PermissionTicketToken ticket, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) { private List<Result> evaluateUserManagedPermissions(AuthorizationRequest request, PermissionTicketToken ticket, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
return authorization.evaluators() return authorization.evaluators()
.from(createPermissions(ticket, request, resourceServer, authorization), evaluationContext) .from(createPermissions(ticket, request, resourceServer, identity, authorization), evaluationContext)
.evaluate(new PermissionTicketAwareDecisionResultCollector(request, ticket, identity, resourceServer, authorization)).results(); .evaluate(new PermissionTicketAwareDecisionResultCollector(request, ticket, identity, resourceServer, authorization)).results();
} }
@ -276,7 +276,7 @@ public class AuthorizationTokenService {
return evaluationContextProvider.apply(authorizationRequest, authorization); return evaluationContextProvider.apply(authorizationRequest, authorization);
} }
private List<ResourcePermission> createPermissions(PermissionTicketToken ticket, AuthorizationRequest request, ResourceServer resourceServer, AuthorizationProvider authorization) { private List<ResourcePermission> createPermissions(PermissionTicketToken ticket, AuthorizationRequest request, ResourceServer resourceServer, KeycloakIdentity identity, AuthorizationProvider authorization) {
StoreFactory storeFactory = authorization.getStoreFactory(); StoreFactory storeFactory = authorization.getStoreFactory();
Map<String, Set<String>> permissionsToEvaluate = new LinkedHashMap<>(); Map<String, Set<String>> permissionsToEvaluate = new LinkedHashMap<>();
ResourceStore resourceStore = storeFactory.getResourceStore(); ResourceStore resourceStore = storeFactory.getResourceStore();
@ -294,17 +294,31 @@ public class AuthorizationTokenService {
requestedScopes = new HashSet<>(); requestedScopes = new HashSet<>();
} }
Resource existingResource = null; List<Resource> existingResources = new ArrayList<>();
if (requestedResource.getResourceId() != null) { if (requestedResource.getResourceId() != null) {
existingResource = resourceStore.findById(requestedResource.getResourceId(), resourceServer.getId()); Resource resource = resourceStore.findById(requestedResource.getResourceId(), resourceServer.getId());
if (existingResource == null) { if (resource != null) {
existingResource = resourceStore.findByName(requestedResource.getResourceId(), resourceServer.getId()); existingResources.add(resource);
} else {
Resource ownerResource = resourceStore.findByName(requestedResource.getResourceId(), identity.getId(), resourceServer.getId());
if (ownerResource != null) {
existingResources.add(ownerResource);
}
if (!identity.isResourceServer()) {
Resource serverResource = resourceStore.findByName(requestedResource.getResourceId(), resourceServer.getId());
if (serverResource != null) {
existingResources.add(serverResource);
}
}
} }
} }
if (existingResource == null && (requestedScopes == null || requestedScopes.isEmpty())) { if (existingResources.isEmpty() && (requestedScopes == null || requestedScopes.isEmpty())) {
throw new CorsErrorResponseException(cors, "invalid_resource", "Resource with id [" + requestedResource.getResourceId() + "] does not exist.", Status.FORBIDDEN); throw new CorsErrorResponseException(cors, "invalid_resource", "Resource with id [" + requestedResource.getResourceId() + "] does not exist.", Status.FORBIDDEN);
} }
@ -314,18 +328,20 @@ public class AuthorizationTokenService {
requestedScopes.addAll(Arrays.asList(clientAdditionalScopes.split(" "))); requestedScopes.addAll(Arrays.asList(clientAdditionalScopes.split(" ")));
} }
if (existingResource != null) { if (!existingResources.isEmpty()) {
Set<String> scopes = permissionsToEvaluate.get(existingResource.getId()); for (Resource resource : existingResources) {
Set<String> scopes = permissionsToEvaluate.get(resource.getId());
if (scopes == null) { if (scopes == null) {
scopes = new HashSet<>(); scopes = new HashSet<>();
permissionsToEvaluate.put(existingResource.getId(), scopes); permissionsToEvaluate.put(resource.getId(), scopes);
if (limit != null) { if (limit != null) {
limit--; limit--;
} }
} }
scopes.addAll(requestedScopes); scopes.addAll(requestedScopes);
}
} else { } else {
List<Resource> resources = resourceStore.findByScope(new ArrayList<>(requestedScopes), ticket.getAudience()[0]); List<Resource> resources = resourceStore.findByScope(new ArrayList<>(requestedScopes), ticket.getAudience()[0]);

View file

@ -224,7 +224,7 @@ public class KeycloakIdentity implements Identity {
return this.accessToken; return this.accessToken;
} }
private boolean isResourceServer() { public boolean isResourceServer() {
UserModel clientUser = null; UserModel clientUser = null;
ClientModel clientModel = getTargetClient(); ClientModel clientModel = getTargetClient();

View file

@ -67,41 +67,61 @@ public class AbstractPermissionService {
private List<ResourceRepresentation> verifyRequestedResource(List<PermissionRequest> request) { private List<ResourceRepresentation> verifyRequestedResource(List<PermissionRequest> request) {
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore(); ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
List<ResourceRepresentation> requestedResources = new ArrayList<>();
return request.stream().map(permissionRequest -> { for (PermissionRequest permissionRequest : request) {
String resourceSetId = permissionRequest.getResourceId(); String resourceSetId = permissionRequest.getResourceId();
Resource resource = null; List<Resource> resources = new ArrayList<>();
if (resourceSetId == null) { if (resourceSetId == null) {
if (permissionRequest.getScopes() == null || permissionRequest.getScopes().isEmpty()) { if (permissionRequest.getScopes() == null || permissionRequest.getScopes().isEmpty()) {
throw new ErrorResponseException("invalid_resource_id", "Resource id or name not provided.", Response.Status.BAD_REQUEST); throw new ErrorResponseException("invalid_resource_id", "Resource id or name not provided.", Response.Status.BAD_REQUEST);
} }
} else { } else {
resource = resourceStore.findById(resourceSetId, resourceServer.getId()); Resource resource = resourceStore.findById(resourceSetId, resourceServer.getId());
if (resource == null) { if (resource != null) {
resource = resourceStore.findByName(resourceSetId, this.resourceServer.getId()); resources.add(resource);
} else {
Resource userResource = resourceStore.findByName(resourceSetId, identity.getId(), this.resourceServer.getId());
if (userResource != null) {
resources.add(userResource);
} }
if (resource == null) { if (!identity.isResourceServer()) {
Resource serverResource = resourceStore.findByName(resourceSetId, this.resourceServer.getId());
if (serverResource != null) {
resources.add(serverResource);
}
}
}
if (resources.isEmpty()) {
throw new ErrorResponseException("invalid_resource_id", "Resource set with id [" + resourceSetId + "] does not exists in this server.", Response.Status.BAD_REQUEST); throw new ErrorResponseException("invalid_resource_id", "Resource set with id [" + resourceSetId + "] does not exists in this server.", Response.Status.BAD_REQUEST);
} }
} }
if (resources.isEmpty()) {
requestedResources.add(new ResourceRepresentation(null, verifyRequestedScopes(permissionRequest, null)));
} else {
for (Resource resource : resources) {
Set<ScopeRepresentation> scopes = verifyRequestedScopes(permissionRequest, resource); Set<ScopeRepresentation> scopes = verifyRequestedScopes(permissionRequest, resource);
if (resource != null) {
ResourceRepresentation representation = new ResourceRepresentation(resource.getName(), scopes); ResourceRepresentation representation = new ResourceRepresentation(resource.getName(), scopes);
representation.setId(resource.getId()); representation.setId(resource.getId());
representation.setOwnerManagedAccess(resource.isOwnerManagedAccess()); representation.setOwnerManagedAccess(resource.isOwnerManagedAccess());
representation.setOwner(new ResourceOwnerRepresentation(resource.getOwner())); representation.setOwner(new ResourceOwnerRepresentation(resource.getOwner()));
return representation; requestedResources.add(representation);
}
}
} }
return new ResourceRepresentation(null, scopes); return requestedResources;
}).collect(Collectors.toList());
} }
private Set<ScopeRepresentation> verifyRequestedScopes(PermissionRequest request, Resource resource) { private Set<ScopeRepresentation> verifyRequestedScopes(PermissionRequest request, Resource resource) {

View file

@ -18,22 +18,26 @@
package org.keycloak.testsuite.admin.client.authorization; package org.keycloak.testsuite.admin.client.authorization;
import org.junit.After; import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.keycloak.admin.client.resource.AuthorizationResource; import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ResourceScopeResource; import org.keycloak.admin.client.resource.ResourceScopeResource;
import org.keycloak.admin.client.resource.ResourceScopesResource; import org.keycloak.admin.client.resource.ResourceScopesResource;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.testsuite.ProfileAssume; import org.keycloak.testsuite.ProfileAssume;
import org.keycloak.testsuite.admin.client.AbstractClientTest; import org.keycloak.testsuite.admin.client.AbstractClientTest;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.util.List;
/** /**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@ -42,24 +46,32 @@ public abstract class AbstractAuthorizationTest extends AbstractClientTest {
protected static final String RESOURCE_SERVER_CLIENT_ID = "resource-server-test"; protected static final String RESOURCE_SERVER_CLIENT_ID = "resource-server-test";
@Override
public void setDefaultPageUriParameters() {
super.setDefaultPageUriParameters();
testRealmPage.setAuthRealm("authz-test");
}
@Override
protected String getRealmId() {
return "authz-test";
}
@BeforeClass @BeforeClass
public static void enabled() { public static void enabled() {
ProfileAssume.assumePreview(); ProfileAssume.assumePreview();
} }
@Before @Override
public void onBeforeAuthzTests() { public void addTestRealms(List<RealmRepresentation> testRealms) {
createOidcClient(RESOURCE_SERVER_CLIENT_ID); testRealms.add(createTestRealm().build());
super.addTestRealms(testRealms);
ClientRepresentation resourceServer = getResourceServer();
assertEquals(RESOURCE_SERVER_CLIENT_ID, resourceServer.getName());
assertFalse(resourceServer.getAuthorizationServicesEnabled());
} }
@After @After
public void onAfterAuthzTests() { public void onAfterReenableAuthorization() {
getClientResource().remove(); enableAuthorizationServices(false);
enableAuthorizationServices(true);
} }
protected ClientResource getClientResource() { protected ClientResource getClientResource() {
@ -70,23 +82,23 @@ public abstract class AbstractAuthorizationTest extends AbstractClientTest {
return findClientRepresentation(RESOURCE_SERVER_CLIENT_ID); return findClientRepresentation(RESOURCE_SERVER_CLIENT_ID);
} }
protected void enableAuthorizationServices() { protected void enableAuthorizationServices(boolean enable) {
ClientRepresentation resourceServer = getResourceServer(); ClientRepresentation resourceServer = getResourceServer();
resourceServer.setAuthorizationServicesEnabled(true); resourceServer.setAuthorizationServicesEnabled(enable);
resourceServer.setServiceAccountsEnabled(true); resourceServer.setServiceAccountsEnabled(true);
resourceServer.setPublicClient(false); resourceServer.setPublicClient(false);
resourceServer.setSecret("secret"); resourceServer.setSecret("secret");
getClientResource().update(resourceServer); getClientResource().update(resourceServer);
if (enable) {
AuthorizationResource authorization = getClientResource().authorization(); AuthorizationResource authorization = getClientResource().authorization();
ResourceServerRepresentation settings = authorization.exportSettings(); ResourceServerRepresentation settings = authorization.exportSettings();
settings.setAllowRemoteResourceManagement(true); settings.setAllowRemoteResourceManagement(true);
authorization.update(settings); authorization.update(settings);
} }
}
protected ResourceScopeResource createDefaultScope() { protected ResourceScopeResource createDefaultScope() {
return createScope("Test Scope", "Scope Icon"); return createScope("Test Scope", "Scope Icon");
@ -108,4 +120,17 @@ public abstract class AbstractAuthorizationTest extends AbstractClientTest {
return resources.scope(stored.getId()); return resources.scope(stored.getId());
} }
private RealmBuilder createTestRealm() {
return RealmBuilder.create().name("authz-test")
.user(UserBuilder.create().username("marta").password("password"))
.user(UserBuilder.create().username("kolo").password("password"))
.client(ClientBuilder.create().clientId(RESOURCE_SERVER_CLIENT_ID)
.name(RESOURCE_SERVER_CLIENT_ID)
.secret("secret")
.authorizationServicesEnabled(true)
.redirectUris("http://localhost/" + RESOURCE_SERVER_CLIENT_ID)
.defaultRoles("uma_protection")
.directAccessGrants());
}
} }

View file

@ -24,6 +24,7 @@ import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation; import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
@ -44,17 +45,38 @@ public class AuthorizationTest extends AbstractAuthorizationTest {
ClientResource clientResource = getClientResource(); ClientResource clientResource = getClientResource();
ClientRepresentation resourceServer = getResourceServer(); ClientRepresentation resourceServer = getResourceServer();
enableAuthorizationServices(); enableAuthorizationServices(false);
enableAuthorizationServices(true);
clientResource.authorization().resources().create(new ResourceRepresentation("Should be removed"));
JSPolicyRepresentation policy = new JSPolicyRepresentation();
policy.setName("should be removed");
policy.setCode("");
clientResource.authorization().policies().js().create(policy);
List<ResourceRepresentation> defaultResources = clientResource.authorization().resources().resources();
assertEquals(2, defaultResources.size());
List<PolicyRepresentation> defaultPolicies = clientResource.authorization().policies().policies();
assertEquals(3, defaultPolicies.size());
enableAuthorizationServices(false);
enableAuthorizationServices(true);
ResourceServerRepresentation settings = clientResource.authorization().getSettings(); ResourceServerRepresentation settings = clientResource.authorization().getSettings();
assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING.name(), settings.getPolicyEnforcementMode().name()); assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING.name(), settings.getPolicyEnforcementMode().name());
assertEquals(resourceServer.getId(), settings.getClientId()); assertEquals(resourceServer.getId(), settings.getClientId());
List<ResourceRepresentation> defaultResources = clientResource.authorization().resources().resources(); defaultResources = clientResource.authorization().resources().resources();
assertEquals(1, defaultResources.size()); assertEquals(1, defaultResources.size());
List<PolicyRepresentation> defaultPolicies = clientResource.authorization().policies().policies(); defaultPolicies = clientResource.authorization().policies().policies();
assertEquals(2, defaultPolicies.size()); assertEquals(2, defaultPolicies.size());
} }
@ -72,8 +94,6 @@ public class AuthorizationTest extends AbstractAuthorizationTest {
ClientResource clientResource = getClientResource(); ClientResource clientResource = getClientResource();
ClientRepresentation resourceServer = getResourceServer(); ClientRepresentation resourceServer = getResourceServer();
enableAuthorizationServices();
ResourceServerRepresentation settings = clientResource.authorization().getSettings(); ResourceServerRepresentation settings = clientResource.authorization().getSettings();
assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING.name(), settings.getPolicyEnforcementMode().name()); assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING.name(), settings.getPolicyEnforcementMode().name());

View file

@ -46,8 +46,6 @@ public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
String permissionName = "resource-based-permission"; String permissionName = "resource-based-permission";
ClientResource clientResource = getClientResource(); ClientResource clientResource = getClientResource();
enableAuthorizationServices();
AuthorizationResource authorizationResource = clientResource.authorization(); AuthorizationResource authorizationResource = clientResource.authorization();
//get Default Resource //get Default Resource
@ -89,8 +87,6 @@ public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
@Test @Test
public void testRoleBasedPolicy() { public void testRoleBasedPolicy() {
ClientResource clientResource = getClientResource(); ClientResource clientResource = getClientResource();
enableAuthorizationServices();
AuthorizationResource authorizationResource = clientResource.authorization(); AuthorizationResource authorizationResource = clientResource.authorization();
ClientRepresentation account = testRealmResource().clients().findByClientId("account").get(0); ClientRepresentation account = testRealmResource().clients().findByClientId("account").get(0);
@ -121,8 +117,6 @@ public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
@Test @Test
public void testRoleBasedPolicyWithMultipleRoles() { public void testRoleBasedPolicyWithMultipleRoles() {
ClientResource clientResource = getClientResource(); ClientResource clientResource = getClientResource();
enableAuthorizationServices();
AuthorizationResource authorizationResource = clientResource.authorization(); AuthorizationResource authorizationResource = clientResource.authorization();
testRealmResource().clients().create(ClientBuilder.create().clientId("test-client-1").defaultRoles("client-role").build()).close(); testRealmResource().clients().create(ClientBuilder.create().clientId("test-client-1").defaultRoles("client-role").build()).close();

View file

@ -54,19 +54,6 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
private static final String[] EXPECTED_BUILTIN_POLICY_PROVIDERS = {"test", "user", "role", "rules", "js", "time", "aggregate", "scope", "resource"}; private static final String[] EXPECTED_BUILTIN_POLICY_PROVIDERS = {"test", "user", "role", "rules", "js", "time", "aggregate", "scope", "resource"};
@Before
@Override
public void onBeforeAuthzTests() {
super.onBeforeAuthzTests();
enableAuthorizationServices();
}
@After
@Override
public void onAfterAuthzTests() {
super.onAfterAuthzTests();
}
@Test @Test
public void testCreate() { public void testCreate() {
PolicyRepresentation newPolicy = createTestingPolicy().toRepresentation(); PolicyRepresentation newPolicy = createTestingPolicy().toRepresentation();

View file

@ -48,24 +48,9 @@ public class ImportAuthorizationSettingsTest extends AbstractAuthorizationTest {
testRealmResource().users().create(UserBuilder.create().username("alice").build()); testRealmResource().users().create(UserBuilder.create().username("alice").build());
} }
@After
public void onAfterAuthzTests() {
ClientResource clientResource = getClientResource();
// Needed to disable authz first. TODO: Looks like a bug. Check later...
ClientRepresentation client = clientResource.toRepresentation();
client.setAuthorizationServicesEnabled(false);
clientResource.update(client);
getClientResource().remove();
}
@Test @Test
public void testImportUnorderedSettings() throws Exception { public void testImportUnorderedSettings() throws Exception {
ClientResource clientResource = getClientResource(); ClientResource clientResource = getClientResource();
enableAuthorizationServices();
ResourceServerRepresentation toImport = JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/import-authorization-unordered-settings.json"), ResourceServerRepresentation.class); ResourceServerRepresentation toImport = JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/import-authorization-unordered-settings.json"), ResourceServerRepresentation.class);
realmsResouce().realm(getRealmId()).roles().create(new RoleRepresentation("user", null, false)); realmsResouce().realm(getRealmId()).roles().create(new RoleRepresentation("user", null, false));
@ -75,6 +60,6 @@ public class ImportAuthorizationSettingsTest extends AbstractAuthorizationTest {
authorizationResource.importSettings(toImport); authorizationResource.importSettings(toImport);
assertEquals(15, authorizationResource.policies().policies().size()); assertEquals(13, authorizationResource.policies().policies().size());
} }
} }

View file

@ -18,12 +18,11 @@
package org.keycloak.testsuite.admin.client.authorization; package org.keycloak.testsuite.admin.client.authorization;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.admin.client.resource.ResourceResource; import org.keycloak.admin.client.resource.ResourceResource;
import org.keycloak.admin.client.resource.ResourcesResource; import org.keycloak.admin.client.resource.ResourcesResource;
import org.keycloak.authorization.client.util.HttpResponseException; import org.keycloak.authorization.client.util.HttpResponseException;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@ -35,6 +34,7 @@ import java.util.Set;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@ -45,33 +45,6 @@ import static org.junit.Assert.fail;
*/ */
public class ResourceManagementTest extends AbstractAuthorizationTest { public class ResourceManagementTest extends AbstractAuthorizationTest {
@Before
@Override
public void onBeforeAuthzTests() {
super.onBeforeAuthzTests();
enableAuthorizationServices();
}
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
RealmRepresentation testRealmRep = new RealmRepresentation();
testRealmRep.setId("authz-test");
testRealmRep.setRealm("authz-test");
testRealmRep.setEnabled(true);
testRealms.add(testRealmRep);
}
@Override
public void setDefaultPageUriParameters() {
super.setDefaultPageUriParameters();
testRealmPage.setAuthRealm("authz-test");
}
@Override
protected String getRealmId() {
return "authz-test";
}
@Test @Test
public void testCreate() { public void testCreate() {
ResourceRepresentation newResource = createResource(); ResourceRepresentation newResource = createResource();
@ -102,6 +75,28 @@ public class ResourceManagementTest extends AbstractAuthorizationTest {
assertEquals("Test Resource Another", newResource.getName()); assertEquals("Test Resource Another", newResource.getName());
} }
@Test
public void failCreateWithSameNameDifferentOwner() {
ResourceRepresentation martaResource = createResource("Resource A", "marta", null, null, null);
ResourceRepresentation koloResource = createResource("Resource A", "kolo", null, null, null);
assertNotNull(martaResource.getId());
assertNotNull(koloResource.getId());
assertNotEquals(martaResource.getId(), koloResource.getId());
assertEquals(2, getClientResource().authorization().resources().findByName(martaResource.getName()).size());
List<ResourceRepresentation> martaResources = getClientResource().authorization().resources().findByName(martaResource.getName(), "marta");
assertEquals(1, martaResources.size());
assertEquals(martaResource.getId(), martaResources.get(0).getId());
List<ResourceRepresentation> koloResources = getClientResource().authorization().resources().findByName(martaResource.getName(), "kolo");
assertEquals(1, koloResources.size());
assertEquals(koloResource.getId(), koloResources.get(0).getId());
}
@Test @Test
public void testUpdate() { public void testUpdate() {
ResourceRepresentation resource = createResource(); ResourceRepresentation resource = createResource();
@ -198,12 +193,17 @@ public class ResourceManagementTest extends AbstractAuthorizationTest {
} }
private ResourceRepresentation createResource() { private ResourceRepresentation createResource() {
return createResource("Test Resource", null, "/test/*", "test-resource", "icon-test-resource");
}
private ResourceRepresentation createResource(String name, String owner, String uri, String type, String iconUri) {
ResourceRepresentation newResource = new ResourceRepresentation(); ResourceRepresentation newResource = new ResourceRepresentation();
newResource.setName("Test Resource"); newResource.setName(name);
newResource.setUri("/test/*"); newResource.setUri(uri);
newResource.setType("test-resource"); newResource.setType(type);
newResource.setIconUri("icon-test-resource"); newResource.setIconUri(iconUri);
newResource.setOwner(owner != null ? new ResourceOwnerRepresentation(owner) : null);
return doCreateResource(newResource); return doCreateResource(newResource);
} }

View file

@ -33,13 +33,6 @@ import static org.junit.Assert.assertEquals;
*/ */
public class ScopeManagementTest extends AbstractAuthorizationTest { public class ScopeManagementTest extends AbstractAuthorizationTest {
@Before
@Override
public void onBeforeAuthzTests() {
super.onBeforeAuthzTests();
enableAuthorizationServices();
}
@Test @Test
public void testCreate() { public void testCreate() {
ScopeRepresentation newScope = createDefaultScope().toRepresentation(); ScopeRepresentation newScope = createDefaultScope().toRepresentation();

View file

@ -127,7 +127,7 @@ public class AuthorizationAPITest extends AbstractAuthzTest {
assertEquals("resource-server-test", rpt.getAudience()[0]); assertEquals("resource-server-test", rpt.getAudience()[0]);
} }
private RealmResource getRealm() throws Exception { private RealmResource getRealm() {
return adminClient.realm("authz-test"); return adminClient.realm("authz-test");
} }

View file

@ -0,0 +1,229 @@
/*
* 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.authz;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.ws.rs.core.Response;
import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.ResourcesResource;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessToken.Authorization;
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.ResourceOwnerRepresentation;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.RoleBuilder;
import org.keycloak.testsuite.util.RolesBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class AuthorizationTest extends AbstractAuthzTest {
private AuthzClient authzClient;
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
testRealms.add(RealmBuilder.create().name("authz-test")
.roles(RolesBuilder.create().realmRole(RoleBuilder.create().name("uma_authorization").build()))
.user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization"))
.user(UserBuilder.create().username("kolo").password("password"))
.client(ClientBuilder.create().clientId("resource-server-test")
.secret("secret")
.authorizationServicesEnabled(true)
.redirectUris("http://localhost/resource-server-test")
.defaultRoles("uma_protection")
.directAccessGrants())
.client(ClientBuilder.create().clientId("test-client")
.secret("secret")
.authorizationServicesEnabled(true)
.redirectUris("http://localhost/test-client")
.directAccessGrants())
.build());
}
@Before
public void configureAuthorization() throws Exception {
ClientResource client = getClient();
AuthorizationResource authorization = client.authorization();
JSPolicyRepresentation policy = new JSPolicyRepresentation();
policy.setName("Grant Policy");
policy.setCode("$evaluation.grant();");
authorization.policies().js().create(policy).close();
policy = new JSPolicyRepresentation();
policy.setName("Deny Policy");
policy.setCode("$evaluation.deny();");
}
@After
public void onAfter() {
ResourcesResource resources = getClient().authorization().resources();
List<ResourceRepresentation> existingResources = resources.resources();
for (ResourceRepresentation resource : existingResources) {
resources.resource(resource.getId()).remove();
}
}
@Test
public void testResourceWithSameNameDifferentOwner() throws JWSInputException {
ResourceRepresentation koloResource = createResource("Resource A", "kolo", "Scope A", "Scope B");
createResourcePermission(koloResource, "Grant Policy");
ResourceRepresentation martaResource = createResource("Resource A", "marta", "Scope A", "Scope B");
createResourcePermission(martaResource, "Grant Policy");
assertNotEquals(koloResource.getId(), martaResource.getId());
AuthorizationRequest request = new AuthorizationRequest();
request.addPermission("Resource A");
List<Permission> permissions = authorize("kolo", "password", request);
assertEquals(1, permissions.size());
Permission permission = permissions.get(0);
assertTrue(permission.getScopes().containsAll(Arrays.asList("Scope A", "Scope B")));
assertEquals(koloResource.getId(), permission.getResourceId());
permissions = authorize("marta", "password", request);
assertEquals(1, permissions.size());
permission = permissions.get(0);
assertEquals(martaResource.getId(), permission.getResourceId());
assertTrue(permission.getScopes().containsAll(Arrays.asList("Scope A", "Scope B")));
}
@Test
public void testResourceServerWithSameNameDifferentOwner() {
ResourceRepresentation koloResource = createResource("Resource A", "kolo", "Scope A", "Scope B");
createResourcePermission(koloResource, "Grant Policy");
ResourceRepresentation serverResource = createResource("Resource A", null, "Scope A", "Scope B");
createResourcePermission(serverResource, "Grant Policy");
AuthorizationRequest request = new AuthorizationRequest();
request.addPermission("Resource A");
List<Permission> permissions = authorize("kolo", "password", request);
assertEquals(2, permissions.size());
for (Permission permission : permissions) {
assertTrue(permission.getResourceId().equals(koloResource.getId()) || permission.getResourceId().equals(serverResource.getId()));
assertEquals("Resource A", permission.getResourceName());
}
}
private List<Permission> authorize(String userName, String password, AuthorizationRequest request) {
AuthorizationResponse response = getAuthzClient().authorization(userName, password).authorize(request);
AccessToken token = toAccessToken(response.getToken());
Authorization authorization = token.getAuthorization();
return authorization.getPermissions();
}
private void createResourcePermission(ResourceRepresentation resource, String... policies) {
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
permission.setName(resource.getName() + UUID.randomUUID().toString());
permission.addResource(resource.getId());
permission.addPolicy(policies);
Response response = getClient().authorization().permissions().resource().create(permission);
assertEquals(201, response.getStatus());
}
@NotNull
private ResourceRepresentation createResource(String name, String owner, String... scopes) {
ResourceRepresentation resource = new ResourceRepresentation();
resource.setName(name);
resource.setOwner(owner != null ? new ResourceOwnerRepresentation(owner) : null);
resource.addScope(scopes);
Response response = getClient().authorization().resources().create(resource);
ResourceRepresentation stored = response.readEntity(ResourceRepresentation.class);
response.close();
resource.setId(stored.getId());
return resource;
}
private RealmResource getRealm() {
return adminClient.realm("authz-test");
}
private ClientResource getClient() {
ClientsResource clients = getRealm().clients();
return clients.findByClientId("resource-server-test").stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
}
private AuthzClient getAuthzClient() {
if (authzClient == null) {
try {
authzClient = AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/default-keycloak.json"), Configuration.class));
} catch (IOException cause) {
throw new RuntimeException("Failed to create authz client", cause);
}
}
return authzClient;
}
}

View file

@ -52,7 +52,7 @@ public class PermissionManagementTest extends AbstractResourceServerTest {
public void testCreatePermissionTicketWithResourceName() throws Exception { public void testCreatePermissionTicketWithResourceName() throws Exception {
ResourceRepresentation resource = addResource("Resource A", "kolo", true); ResourceRepresentation resource = addResource("Resource A", "kolo", true);
AuthzClient authzClient = getAuthzClient(); AuthzClient authzClient = getAuthzClient();
PermissionResponse response = authzClient.protection("marta", "password").permission().create(new PermissionRequest(resource.getName())); PermissionResponse response = authzClient.protection("marta", "password").permission().create(new PermissionRequest(resource.getId()));
AuthorizationRequest request = new AuthorizationRequest(); AuthorizationRequest request = new AuthorizationRequest();
request.setTicket(response.getTicket()); request.setTicket(response.getTicket());
request.setClaimToken(authzClient.obtainAccessToken("marta", "password").getToken()); request.setClaimToken(authzClient.obtainAccessToken("marta", "password").getToken());
@ -125,7 +125,7 @@ public class PermissionManagementTest extends AbstractResourceServerTest {
@Test @Test
public void testDeleteScopeAndPermissionTicket() throws Exception { public void testDeleteScopeAndPermissionTicket() throws Exception {
ResourceRepresentation resource = addResource("Resource A", "kolo", true, "ScopeA", "ScopeB", "ScopeC"); ResourceRepresentation resource = addResource("Resource A", "kolo", true, "ScopeA", "ScopeB", "ScopeC");
PermissionRequest permissionRequest = new PermissionRequest(resource.getName()); PermissionRequest permissionRequest = new PermissionRequest(resource.getId());
permissionRequest.setScopes(new HashSet<>(Arrays.asList("ScopeA", "ScopeB", "ScopeC"))); permissionRequest.setScopes(new HashSet<>(Arrays.asList("ScopeA", "ScopeB", "ScopeC")));
@ -164,7 +164,7 @@ public class PermissionManagementTest extends AbstractResourceServerTest {
@Test @Test
public void testRemoveScopeFromResource() throws Exception { public void testRemoveScopeFromResource() throws Exception {
ResourceRepresentation resource = addResource("Resource A", "kolo", true, "ScopeA", "ScopeB"); ResourceRepresentation resource = addResource("Resource A", "kolo", true, "ScopeA", "ScopeB");
PermissionRequest permissionRequest = new PermissionRequest(resource.getName(), "ScopeA", "ScopeB"); PermissionRequest permissionRequest = new PermissionRequest(resource.getId(), "ScopeA", "ScopeB");
AuthzClient authzClient = getAuthzClient(); AuthzClient authzClient = getAuthzClient();
PermissionResponse response = authzClient.protection("marta", "password").permission().create(permissionRequest); PermissionResponse response = authzClient.protection("marta", "password").permission().create(permissionRequest);
@ -255,7 +255,7 @@ public class PermissionManagementTest extends AbstractResourceServerTest {
getClient(getRealm()).authorization().resources().resource(resourceA.getId()).update(resourceA); getClient(getRealm()).authorization().resources().resource(resourceA.getId()).update(resourceA);
PermissionRequest permissionRequest = new PermissionRequest("Resource A"); PermissionRequest permissionRequest = new PermissionRequest(resourceA.getId());
permissionRequest.setScopes(new HashSet<>(Arrays.asList("ScopeA", "ScopeC"))); permissionRequest.setScopes(new HashSet<>(Arrays.asList("ScopeA", "ScopeC")));

View file

@ -123,7 +123,7 @@ public class UmaGrantTypeTest extends AbstractResourceServerTest {
ResourceRepresentation resourceA = addResource("Resource Marta", "marta", true, "ScopeA", "ScopeB", "ScopeC"); ResourceRepresentation resourceA = addResource("Resource Marta", "marta", true, "ScopeA", "ScopeB", "ScopeC");
permission.setName(resourceA.getName() + " Permission"); permission.setName(resourceA.getName() + " Permission");
permission.addResource(resourceA.getName()); permission.addResource(resourceA.getId());
permission.addPolicy("Default Policy"); permission.addPolicy("Default Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close(); getClient(getRealm()).authorization().permissions().resource().create(permission).close();
@ -131,7 +131,7 @@ public class UmaGrantTypeTest extends AbstractResourceServerTest {
ResourceRepresentation resourceB = addResource("Resource B", "marta", "ScopeA", "ScopeB", "ScopeC"); ResourceRepresentation resourceB = addResource("Resource B", "marta", "ScopeA", "ScopeB", "ScopeC");
permission.setName(resourceB.getName() + " Permission"); permission.setName(resourceB.getName() + " Permission");
permission.addResource(resourceB.getName()); permission.addResource(resourceB.getId());
permission.addPolicy("Default Policy"); permission.addPolicy("Default Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close(); getClient(getRealm()).authorization().permissions().resource().create(permission).close();

View file

@ -68,7 +68,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB"); resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB");
permission.setName(resource.getName() + " Permission"); permission.setName(resource.getName() + " Permission");
permission.addResource(resource.getName()); permission.addResource(resource.getId());
permission.addPolicy("Only Owner Policy"); permission.addPolicy("Only Owner Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close(); getClient(getRealm()).authorization().permissions().resource().create(permission).close();
@ -91,7 +91,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(permissions.isEmpty()); assertTrue(permissions.isEmpty());
try { try {
response = authorize("kolo", "password", resource.getName(), new String[] {"ScopeA", "ScopeB"}); response = authorize("kolo", "password", resource.getId(), new String[] {"ScopeA", "ScopeB"});
fail("User should have access to resource from another user"); fail("User should have access to resource from another user");
} catch (AuthorizationDeniedException ade) { } catch (AuthorizationDeniedException ade) {
@ -104,7 +104,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB"); resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB");
permission.setName(resource.getName() + " Permission"); permission.setName(resource.getName() + " Permission");
permission.addResource(resource.getName()); permission.addResource(resource.getId());
permission.addPolicy("Only Owner Policy"); permission.addPolicy("Only Owner Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close(); getClient(getRealm()).authorization().permissions().resource().create(permission).close();
@ -127,7 +127,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(permissions.isEmpty()); assertTrue(permissions.isEmpty());
try { try {
response = authorize("kolo", "password", "Resource A", new String[] {}); response = authorize("kolo", "password", resource.getId(), new String[] {});
fail("User should have access to resource from another user"); fail("User should have access to resource from another user");
} catch (AuthorizationDeniedException ade) { } catch (AuthorizationDeniedException ade) {
@ -156,7 +156,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(ticket.isGranted()); assertTrue(ticket.isGranted());
} }
response = authorize("kolo", "password", resource.getName(), new String[] {"ScopeA", "ScopeB"}); response = authorize("kolo", "password", resource.getId(), new String[] {"ScopeA", "ScopeB"});
rpt = response.getToken(); rpt = response.getToken();
assertNotNull(rpt); assertNotNull(rpt);
@ -180,7 +180,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
resource = addResource("Resource A", "marta", true); resource = addResource("Resource A", "marta", true);
permission.setName(resource.getName() + " Permission"); permission.setName(resource.getName() + " Permission");
permission.addResource(resource.getName()); permission.addResource(resource.getId());
permission.addPolicy("Only Owner Policy"); permission.addPolicy("Only Owner Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close(); getClient(getRealm()).authorization().permissions().resource().create(permission).close();
@ -203,7 +203,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(permissions.isEmpty()); assertTrue(permissions.isEmpty());
try { try {
response = authorize("kolo", "password", "Resource A", new String[] {}); response = authorize("kolo", "password", resource.getId(), new String[] {});
fail("User should have access to resource from another user"); fail("User should have access to resource from another user");
} catch (AuthorizationDeniedException ade) { } catch (AuthorizationDeniedException ade) {
@ -232,7 +232,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(ticket.isGranted()); assertTrue(ticket.isGranted());
} }
response = authorize("kolo", "password", resource.getName(), new String[] {}); response = authorize("kolo", "password", resource.getId(), new String[] {});
rpt = response.getToken(); rpt = response.getToken();
assertNotNull(rpt); assertNotNull(rpt);
@ -249,7 +249,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertPermissions(permissions, resource.getName()); assertPermissions(permissions, resource.getName());
assertTrue(permissions.isEmpty()); assertTrue(permissions.isEmpty());
response = authorize("kolo", "password", resource.getName(), new String[] {}); response = authorize("kolo", "password", resource.getId(), new String[] {});
rpt = response.getToken(); rpt = response.getToken();
assertNotNull(rpt); assertNotNull(rpt);
@ -282,7 +282,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB"); resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB");
permission.setName(resource.getName() + " Permission"); permission.setName(resource.getName() + " Permission");
permission.addResource(resource.getName()); permission.addResource(resource.getId());
permission.addPolicy("Only Owner Policy"); permission.addPolicy("Only Owner Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close(); getClient(getRealm()).authorization().permissions().resource().create(permission).close();
@ -305,7 +305,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(permissions.isEmpty()); assertTrue(permissions.isEmpty());
try { try {
response = authorize("kolo", "password", "Resource A", new String[] {"ScopeA"}); response = authorize("kolo", "password", resource.getId(), new String[] {"ScopeA"});
fail("User should have access to resource from another user"); fail("User should have access to resource from another user");
} catch (AuthorizationDeniedException ade) { } catch (AuthorizationDeniedException ade) {
@ -324,7 +324,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
permissionResource.update(ticket); permissionResource.update(ticket);
response = authorize("kolo", "password", resource.getName(), new String[] {"ScopeA", "ScopeB"}); response = authorize("kolo", "password", resource.getId(), new String[] {"ScopeA", "ScopeB"});
rpt = response.getToken(); rpt = response.getToken();
assertNotNull(rpt); assertNotNull(rpt);