diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java b/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
index 1bf5c8697f..df24fdba95 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
@@ -97,7 +97,7 @@ public class AuthzClient {
* @return a {@link ProtectionResource}
*/
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}
*/
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
public String call() {
return accessToken;
@@ -128,7 +128,7 @@ public class AuthzClient {
* @return a {@link ProtectionResource}
*/
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));
}
/**
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
index cc92712726..cf2e91a5f6 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
@@ -22,6 +22,7 @@ import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
+import org.keycloak.authorization.client.Configuration;
import org.keycloak.authorization.client.representation.ResourceRepresentation;
import org.keycloak.authorization.client.representation.ServerConfiguration;
import org.keycloak.authorization.client.util.Http;
@@ -38,11 +39,13 @@ public class ProtectedResource {
private final Http http;
private ServerConfiguration serverConfiguration;
+ private final Configuration configuration;
private final TokenCallable pat;
- ProtectedResource(Http http, ServerConfiguration serverConfiguration, TokenCallable pat) {
+ ProtectedResource(Http http, ServerConfiguration serverConfiguration, Configuration configuration, TokenCallable pat) {
this.http = http;
this.serverConfiguration = serverConfiguration;
+ this.configuration = configuration;
this.pat = pat;
}
@@ -119,13 +122,30 @@ public class ProtectedResource {
}
/**
- * Query the server for a resource given its name
.
+ * Query the server for a resource given its name
where the owner is the resource server itself.
*
* @param id the resource name
* @return a {@link ResourceRepresentation}
*/
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 name
and a given ownerId
.
+ *
+ * @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) {
return null;
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
index 7268fe954a..03fa9451ae 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
@@ -17,6 +17,7 @@
*/
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.TokenIntrospectionResponse;
import org.keycloak.authorization.client.util.Http;
@@ -31,15 +32,17 @@ public class ProtectionResource {
private final TokenCallable pat;
private final Http http;
+ private final Configuration configuration;
private ServerConfiguration serverConfiguration;
- public ProtectionResource(Http http, ServerConfiguration serverConfiguration, TokenCallable pat) {
+ public ProtectionResource(Http http, ServerConfiguration serverConfiguration, Configuration configuration, TokenCallable pat) {
if (pat == null) {
throw new RuntimeException("No access token was provided when creating client for Protection API.");
}
this.http = http;
this.serverConfiguration = serverConfiguration;
+ this.configuration = configuration;
this.pat = pat;
}
@@ -49,7 +52,7 @@ public class ProtectionResource {
* @return a {@link ProtectedResource}
*/
public ProtectedResource resource() {
- return new ProtectedResource(http, serverConfiguration, pat);
+ return new ProtectedResource(http, serverConfiguration, configuration, pat);
}
/**
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java
index 3531f40a5f..ed20a67fed 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java
@@ -44,4 +44,12 @@ public class HttpResponseException extends RuntimeException {
public byte[] getBytes() {
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();
+ }
}
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java
index ae2eaf11a6..2b0b79a712 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java
@@ -39,7 +39,7 @@ public final class Throwables {
*/
public static RuntimeException handleWrapException(String message, Throwable cause) {
if (cause instanceof HttpResponseException) {
- throw handleAndWrapHttpResponseException(message, HttpResponseException.class.cast(cause));
+ throw handleAndWrapHttpResponseException(HttpResponseException.class.cast(cause));
}
return new RuntimeException(message, cause);
@@ -91,19 +91,11 @@ public final class Throwables {
throw new RuntimeException(message, cause);
}
- private static RuntimeException handleAndWrapHttpResponseException(String message, 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));
- }
-
+ private static RuntimeException handleAndWrapHttpResponseException(HttpResponseException exception) {
if (403 == exception.getStatusCode()) {
- throw new AuthorizationDeniedException(detail.toString(), exception);
+ throw new AuthorizationDeniedException(exception);
}
- return new RuntimeException(detail.toString(), exception);
+ return new RuntimeException(exception);
}
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java
index e7daaa18e8..7f2c39457b 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java
@@ -59,6 +59,11 @@ public interface ResourcesResource {
@Produces(MediaType.APPLICATION_JSON)
List findByName(@QueryParam("name") String name);
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ List findByName(@QueryParam("name") String name, @QueryParam("owner") String owner);
+
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
index 0f245eeaa5..e54316d022 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
@@ -79,7 +79,7 @@ public class StoreFactoryCacheManager extends CacheManager {
public void resourceUpdated(String id, String name, String type, String uri, Set scopes, String serverId, String owner, Set invalidations) {
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, null));
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByResource(id, serverId));
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
index c50cec1ec7..5924573df6 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
@@ -336,8 +336,8 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
return "scope.name." + name + "." + serverId;
}
- public static String getResourceByNameCacheKey(String name, String serverId) {
- return "resource.name." + name + "." + serverId;
+ public static String getResourceByNameCacheKey(String name, String ownerId, String serverId) {
+ return "resource.name." + name + "." + ownerId + "." + serverId;
}
public static String getResourceByOwnerCacheKey(String owner, String serverId) {
@@ -580,17 +580,22 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
@Override
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;
- String cacheKey = getResourceByNameCacheKey(name, resourceServerId);
+ String cacheKey = getResourceByNameCacheKey(name, ownerId, resourceServerId);
List result = cacheQuery(cacheKey, ResourceListQuery.class, () -> {
- Resource resource = getResourceStoreDelegate().findByName(name, resourceServerId);
+ Resource resource = getResourceStoreDelegate().findByName(name, ownerId, resourceServerId);
- if (resource == null) {
- return Collections.emptyList();
- }
+ if (resource == null) {
+ return Collections.emptyList();
+ }
- return Arrays.asList(resource);
- },
+ return Arrays.asList(resource);
+ },
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
if (result.isEmpty()) {
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java
index 6031a6e92a..861853a32d 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java
@@ -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="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="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="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))"),
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
index c938afe239..0c82dc01de 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
@@ -237,11 +237,17 @@ public class JPAResourceStore implements ResourceStore {
@Override
public Resource findByName(String name, String resourceServerId) {
+ return findByName(name, resourceServerId, resourceServerId);
+ }
+
+ @Override
+ public Resource findByName(String name, String ownerId, String resourceServerId) {
TypedQuery query = entityManager.createNamedQuery("findResourceIdByName", String.class);
query.setFlushMode(FlushModeType.COMMIT);
query.setParameter("serverId", resourceServerId);
query.setParameter("name", name);
+ query.setParameter("ownerId", ownerId);
try {
String id = query.getSingleResult();
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
index 0f58741fe2..88c898b3ce 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
@@ -285,7 +285,7 @@ public final class AuthorizationProvider implements Provider {
}
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();
@@ -459,6 +459,11 @@ public final class AuthorizationProvider implements Provider {
return delegate.findByName(name, resourceServerId);
}
+ @Override
+ public Resource findByName(String name, String ownerId, String resourceServerId) {
+ return delegate.findByName(name, ownerId, resourceServerId);
+ }
+
@Override
public List findByType(String type, String resourceServerId) {
return delegate.findByType(type, resourceServerId);
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java
index 708f7e1c5d..df7c705eb6 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java
@@ -117,7 +117,7 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
Resource resource = resourceStore.findById(permission.getResourceId(), resourceServer.getId());
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())) {
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java
index 9a2ac51609..fd6f85c4d1 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java
@@ -97,7 +97,7 @@ public interface ResourceStore {
List findByScope(List 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 resourceServerId the identifier of the resource server
@@ -105,6 +105,16 @@ public interface ResourceStore {
*/
Resource findByName(String name, String resourceServerId);
+ /**
+ * Find a {@link Resource} by its name where the owner is the given ownerId
.
+ *
+ * @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.
*
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index c738b818ab..732491383a 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -2275,7 +2275,7 @@ public class RepresentationToModel {
if (resource == null) {
resource = storeFactory.getResourceStore().findByName(resourceId, policy.getResourceServer().getId());
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) {
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();
if (owner == null) {
@@ -2338,12 +2316,6 @@ public class RepresentationToModel {
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)) {
RealmModel realm = authorization.getRealm();
KeycloakSession keycloakSession = authorization.getKeycloakSession();
@@ -2361,6 +2333,28 @@ public class RepresentationToModel {
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);
model.setDisplayName(resource.getDisplayName());
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
index 0e4f9117d1..04c9d74de5 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -118,7 +118,6 @@ public class ResourceSetService {
public Response create(ResourceRepresentation resource, Function toRepresentation) {
requireManage();
StoreFactory storeFactory = this.authorization.getStoreFactory();
- Resource existingResource = storeFactory.getResourceStore().findByName(resource.getName(), this.resourceServer.getId());
ResourceOwnerRepresentation owner = resource.getOwner();
if (owner == null) {
@@ -132,7 +131,9 @@ public class ResourceSetService {
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.");
}
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 a8075a09cb..989d228a9b 100644
--- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
+++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
@@ -172,13 +172,13 @@ public class AuthorizationTokenService {
private List evaluatePermissions(AuthorizationRequest authorizationRequest, PermissionTicketToken ticket, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
return authorization.evaluators()
- .from(createPermissions(ticket, authorizationRequest, resourceServer, authorization), evaluationContext)
+ .from(createPermissions(ticket, authorizationRequest, resourceServer, identity, authorization), evaluationContext)
.evaluate();
}
private List evaluateUserManagedPermissions(AuthorizationRequest request, PermissionTicketToken ticket, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
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();
}
@@ -276,7 +276,7 @@ public class AuthorizationTokenService {
return evaluationContextProvider.apply(authorizationRequest, authorization);
}
- private List createPermissions(PermissionTicketToken ticket, AuthorizationRequest request, ResourceServer resourceServer, AuthorizationProvider authorization) {
+ private List createPermissions(PermissionTicketToken ticket, AuthorizationRequest request, ResourceServer resourceServer, KeycloakIdentity identity, AuthorizationProvider authorization) {
StoreFactory storeFactory = authorization.getStoreFactory();
Map> permissionsToEvaluate = new LinkedHashMap<>();
ResourceStore resourceStore = storeFactory.getResourceStore();
@@ -294,17 +294,31 @@ public class AuthorizationTokenService {
requestedScopes = new HashSet<>();
}
- Resource existingResource = null;
+ List existingResources = new ArrayList<>();
if (requestedResource.getResourceId() != null) {
- existingResource = resourceStore.findById(requestedResource.getResourceId(), resourceServer.getId());
+ Resource resource = resourceStore.findById(requestedResource.getResourceId(), resourceServer.getId());
- if (existingResource == null) {
- existingResource = resourceStore.findByName(requestedResource.getResourceId(), resourceServer.getId());
+ if (resource != null) {
+ 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);
}
@@ -314,18 +328,20 @@ public class AuthorizationTokenService {
requestedScopes.addAll(Arrays.asList(clientAdditionalScopes.split(" ")));
}
- if (existingResource != null) {
- Set scopes = permissionsToEvaluate.get(existingResource.getId());
+ if (!existingResources.isEmpty()) {
+ for (Resource resource : existingResources) {
+ Set scopes = permissionsToEvaluate.get(resource.getId());
- if (scopes == null) {
- scopes = new HashSet<>();
- permissionsToEvaluate.put(existingResource.getId(), scopes);
- if (limit != null) {
- limit--;
+ if (scopes == null) {
+ scopes = new HashSet<>();
+ permissionsToEvaluate.put(resource.getId(), scopes);
+ if (limit != null) {
+ limit--;
+ }
}
- }
- scopes.addAll(requestedScopes);
+ scopes.addAll(requestedScopes);
+ }
} else {
List resources = resourceStore.findByScope(new ArrayList<>(requestedScopes), ticket.getAudience()[0]);
diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
index 654193d0b8..7322eb2270 100644
--- a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
+++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
@@ -224,7 +224,7 @@ public class KeycloakIdentity implements Identity {
return this.accessToken;
}
- private boolean isResourceServer() {
+ public boolean isResourceServer() {
UserModel clientUser = null;
ClientModel clientModel = getTargetClient();
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java b/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
index 7a8e05e27c..7dd34963b5 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
@@ -67,41 +67,61 @@ public class AbstractPermissionService {
private List verifyRequestedResource(List request) {
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
+ List requestedResources = new ArrayList<>();
- return request.stream().map(permissionRequest -> {
+ for (PermissionRequest permissionRequest : request) {
String resourceSetId = permissionRequest.getResourceId();
- Resource resource = null;
+ List resources = new ArrayList<>();
if (resourceSetId == null) {
if (permissionRequest.getScopes() == null || permissionRequest.getScopes().isEmpty()) {
throw new ErrorResponseException("invalid_resource_id", "Resource id or name not provided.", Response.Status.BAD_REQUEST);
}
} else {
- resource = resourceStore.findById(resourceSetId, resourceServer.getId());
+ Resource resource = resourceStore.findById(resourceSetId, resourceServer.getId());
- if (resource == null) {
- resource = resourceStore.findByName(resourceSetId, this.resourceServer.getId());
+ if (resource != null) {
+ resources.add(resource);
+ } else {
+ Resource userResource = resourceStore.findByName(resourceSetId, identity.getId(), this.resourceServer.getId());
+
+ if (userResource != null) {
+ resources.add(userResource);
+ }
+
+ if (!identity.isResourceServer()) {
+ Resource serverResource = resourceStore.findByName(resourceSetId, this.resourceServer.getId());
+
+ if (serverResource != null) {
+ resources.add(serverResource);
+ }
+ }
}
- if (resource == null) {
+ if (resources.isEmpty()) {
throw new ErrorResponseException("invalid_resource_id", "Resource set with id [" + resourceSetId + "] does not exists in this server.", Response.Status.BAD_REQUEST);
}
}
- Set scopes = verifyRequestedScopes(permissionRequest, resource);
+ if (resources.isEmpty()) {
+ requestedResources.add(new ResourceRepresentation(null, verifyRequestedScopes(permissionRequest, null)));
- if (resource != null) {
- ResourceRepresentation representation = new ResourceRepresentation(resource.getName(), scopes);
+ } else {
+ for (Resource resource : resources) {
+ Set scopes = verifyRequestedScopes(permissionRequest, resource);
- representation.setId(resource.getId());
- representation.setOwnerManagedAccess(resource.isOwnerManagedAccess());
- representation.setOwner(new ResourceOwnerRepresentation(resource.getOwner()));
+ ResourceRepresentation representation = new ResourceRepresentation(resource.getName(), scopes);
- return representation;
+ representation.setId(resource.getId());
+ representation.setOwnerManagedAccess(resource.isOwnerManagedAccess());
+ representation.setOwner(new ResourceOwnerRepresentation(resource.getOwner()));
+
+ requestedResources.add(representation);
+ }
}
+ }
- return new ResourceRepresentation(null, scopes);
- }).collect(Collectors.toList());
+ return requestedResources;
}
private Set verifyRequestedScopes(PermissionRequest request, Resource resource) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java
index 51a2caebc9..8546c2a15d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java
@@ -18,22 +18,26 @@
package org.keycloak.testsuite.admin.client.authorization;
import org.junit.After;
-import org.junit.Before;
import org.junit.BeforeClass;
import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ResourceScopeResource;
import org.keycloak.admin.client.resource.ResourceScopesResource;
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.ScopeRepresentation;
import org.keycloak.testsuite.ProfileAssume;
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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+
+import java.util.List;
/**
* @author Pedro Igor
@@ -42,24 +46,32 @@ public abstract class AbstractAuthorizationTest extends AbstractClientTest {
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
public static void enabled() {
ProfileAssume.assumePreview();
}
- @Before
- public void onBeforeAuthzTests() {
- createOidcClient(RESOURCE_SERVER_CLIENT_ID);
-
- ClientRepresentation resourceServer = getResourceServer();
-
- assertEquals(RESOURCE_SERVER_CLIENT_ID, resourceServer.getName());
- assertFalse(resourceServer.getAuthorizationServicesEnabled());
+ @Override
+ public void addTestRealms(List testRealms) {
+ testRealms.add(createTestRealm().build());
+ super.addTestRealms(testRealms);
}
@After
- public void onAfterAuthzTests() {
- getClientResource().remove();
+ public void onAfterReenableAuthorization() {
+ enableAuthorizationServices(false);
+ enableAuthorizationServices(true);
}
protected ClientResource getClientResource() {
@@ -70,22 +82,22 @@ public abstract class AbstractAuthorizationTest extends AbstractClientTest {
return findClientRepresentation(RESOURCE_SERVER_CLIENT_ID);
}
- protected void enableAuthorizationServices() {
+ protected void enableAuthorizationServices(boolean enable) {
ClientRepresentation resourceServer = getResourceServer();
- resourceServer.setAuthorizationServicesEnabled(true);
+ resourceServer.setAuthorizationServicesEnabled(enable);
resourceServer.setServiceAccountsEnabled(true);
resourceServer.setPublicClient(false);
resourceServer.setSecret("secret");
getClientResource().update(resourceServer);
- AuthorizationResource authorization = getClientResource().authorization();
- ResourceServerRepresentation settings = authorization.exportSettings();
-
- settings.setAllowRemoteResourceManagement(true);
-
- authorization.update(settings);
+ if (enable) {
+ AuthorizationResource authorization = getClientResource().authorization();
+ ResourceServerRepresentation settings = authorization.exportSettings();
+ settings.setAllowRemoteResourceManagement(true);
+ authorization.update(settings);
+ }
}
protected ResourceScopeResource createDefaultScope() {
@@ -108,4 +120,17 @@ public abstract class AbstractAuthorizationTest extends AbstractClientTest {
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());
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationTest.java
index 2d549de86b..f642979893 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationTest.java
@@ -24,6 +24,7 @@ import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
import org.keycloak.representations.idm.ClientRepresentation;
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.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
@@ -44,17 +45,38 @@ public class AuthorizationTest extends AbstractAuthorizationTest {
ClientResource clientResource = getClientResource();
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 defaultResources = clientResource.authorization().resources().resources();
+
+ assertEquals(2, defaultResources.size());
+
+ List defaultPolicies = clientResource.authorization().policies().policies();
+
+ assertEquals(3, defaultPolicies.size());
+
+ enableAuthorizationServices(false);
+ enableAuthorizationServices(true);
ResourceServerRepresentation settings = clientResource.authorization().getSettings();
assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING.name(), settings.getPolicyEnforcementMode().name());
assertEquals(resourceServer.getId(), settings.getClientId());
- List defaultResources = clientResource.authorization().resources().resources();
+ defaultResources = clientResource.authorization().resources().resources();
assertEquals(1, defaultResources.size());
- List defaultPolicies = clientResource.authorization().policies().policies();
+ defaultPolicies = clientResource.authorization().policies().policies();
assertEquals(2, defaultPolicies.size());
}
@@ -72,8 +94,6 @@ public class AuthorizationTest extends AbstractAuthorizationTest {
ClientResource clientResource = getClientResource();
ClientRepresentation resourceServer = getResourceServer();
- enableAuthorizationServices();
-
ResourceServerRepresentation settings = clientResource.authorization().getSettings();
assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING.name(), settings.getPolicyEnforcementMode().name());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ExportAuthorizationSettingsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ExportAuthorizationSettingsTest.java
index c71d12f53a..59d655aa46 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ExportAuthorizationSettingsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ExportAuthorizationSettingsTest.java
@@ -46,8 +46,6 @@ public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
String permissionName = "resource-based-permission";
ClientResource clientResource = getClientResource();
-
- enableAuthorizationServices();
AuthorizationResource authorizationResource = clientResource.authorization();
//get Default Resource
@@ -89,8 +87,6 @@ public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
@Test
public void testRoleBasedPolicy() {
ClientResource clientResource = getClientResource();
-
- enableAuthorizationServices();
AuthorizationResource authorizationResource = clientResource.authorization();
ClientRepresentation account = testRealmResource().clients().findByClientId("account").get(0);
@@ -121,8 +117,6 @@ public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
@Test
public void testRoleBasedPolicyWithMultipleRoles() {
ClientResource clientResource = getClientResource();
-
- enableAuthorizationServices();
AuthorizationResource authorizationResource = clientResource.authorization();
testRealmResource().clients().create(ClientBuilder.create().clientId("test-client-1").defaultRoles("client-role").build()).close();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/GenericPolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/GenericPolicyManagementTest.java
index bd4d97351e..322492d5b8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/GenericPolicyManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/GenericPolicyManagementTest.java
@@ -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"};
- @Before
- @Override
- public void onBeforeAuthzTests() {
- super.onBeforeAuthzTests();
- enableAuthorizationServices();
- }
-
- @After
- @Override
- public void onAfterAuthzTests() {
- super.onAfterAuthzTests();
- }
-
@Test
public void testCreate() {
PolicyRepresentation newPolicy = createTestingPolicy().toRepresentation();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java
index a308ec0b33..0524111dc0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ImportAuthorizationSettingsTest.java
@@ -48,24 +48,9 @@ public class ImportAuthorizationSettingsTest extends AbstractAuthorizationTest {
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
public void testImportUnorderedSettings() throws Exception {
ClientResource clientResource = getClientResource();
-
- enableAuthorizationServices();
-
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));
@@ -75,6 +60,6 @@ public class ImportAuthorizationSettingsTest extends AbstractAuthorizationTest {
authorizationResource.importSettings(toImport);
- assertEquals(15, authorizationResource.policies().policies().size());
+ assertEquals(13, authorizationResource.policies().policies().size());
}
}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementTest.java
index 3c1a2f189f..41fcb66eb3 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementTest.java
@@ -18,12 +18,11 @@
package org.keycloak.testsuite.admin.client.authorization;
-import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ResourceResource;
import org.keycloak.admin.client.resource.ResourcesResource;
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.ScopeRepresentation;
@@ -35,6 +34,7 @@ import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -45,33 +45,6 @@ import static org.junit.Assert.fail;
*/
public class ResourceManagementTest extends AbstractAuthorizationTest {
- @Before
- @Override
- public void onBeforeAuthzTests() {
- super.onBeforeAuthzTests();
- enableAuthorizationServices();
- }
-
- @Override
- public void addTestRealms(List 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
public void testCreate() {
ResourceRepresentation newResource = createResource();
@@ -102,6 +75,28 @@ public class ResourceManagementTest extends AbstractAuthorizationTest {
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 martaResources = getClientResource().authorization().resources().findByName(martaResource.getName(), "marta");
+
+ assertEquals(1, martaResources.size());
+ assertEquals(martaResource.getId(), martaResources.get(0).getId());
+
+ List koloResources = getClientResource().authorization().resources().findByName(martaResource.getName(), "kolo");
+
+ assertEquals(1, koloResources.size());
+ assertEquals(koloResource.getId(), koloResources.get(0).getId());
+ }
+
@Test
public void testUpdate() {
ResourceRepresentation resource = createResource();
@@ -198,12 +193,17 @@ public class ResourceManagementTest extends AbstractAuthorizationTest {
}
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();
- newResource.setName("Test Resource");
- newResource.setUri("/test/*");
- newResource.setType("test-resource");
- newResource.setIconUri("icon-test-resource");
+ newResource.setName(name);
+ newResource.setUri(uri);
+ newResource.setType(type);
+ newResource.setIconUri(iconUri);
+ newResource.setOwner(owner != null ? new ResourceOwnerRepresentation(owner) : null);
return doCreateResource(newResource);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopeManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopeManagementTest.java
index bcb8b1b636..9590078048 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopeManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopeManagementTest.java
@@ -33,13 +33,6 @@ import static org.junit.Assert.assertEquals;
*/
public class ScopeManagementTest extends AbstractAuthorizationTest {
- @Before
- @Override
- public void onBeforeAuthzTests() {
- super.onBeforeAuthzTests();
- enableAuthorizationServices();
- }
-
@Test
public void testCreate() {
ScopeRepresentation newScope = createDefaultScope().toRepresentation();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java
index 61d27735c9..fb6fab12de 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java
@@ -127,7 +127,7 @@ public class AuthorizationAPITest extends AbstractAuthzTest {
assertEquals("resource-server-test", rpt.getAudience()[0]);
}
- private RealmResource getRealm() throws Exception {
+ private RealmResource getRealm() {
return adminClient.realm("authz-test");
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationTest.java
new file mode 100644
index 0000000000..16eb0cbf7e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationTest.java
@@ -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 Pedro Igor
+ */
+public class AuthorizationTest extends AbstractAuthzTest {
+
+ private AuthzClient authzClient;
+
+ @Override
+ public void addTestRealms(List 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 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 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 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 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;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java
index 21bb9ee33f..f488d32897 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java
@@ -52,7 +52,7 @@ public class PermissionManagementTest extends AbstractResourceServerTest {
public void testCreatePermissionTicketWithResourceName() throws Exception {
ResourceRepresentation resource = addResource("Resource A", "kolo", true);
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();
request.setTicket(response.getTicket());
request.setClaimToken(authzClient.obtainAccessToken("marta", "password").getToken());
@@ -125,7 +125,7 @@ public class PermissionManagementTest extends AbstractResourceServerTest {
@Test
public void testDeleteScopeAndPermissionTicket() throws Exception {
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")));
@@ -164,7 +164,7 @@ public class PermissionManagementTest extends AbstractResourceServerTest {
@Test
public void testRemoveScopeFromResource() throws Exception {
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();
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);
- PermissionRequest permissionRequest = new PermissionRequest("Resource A");
+ PermissionRequest permissionRequest = new PermissionRequest(resourceA.getId());
permissionRequest.setScopes(new HashSet<>(Arrays.asList("ScopeA", "ScopeC")));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java
index 41448cf175..5d4c1a12c1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java
@@ -123,7 +123,7 @@ public class UmaGrantTypeTest extends AbstractResourceServerTest {
ResourceRepresentation resourceA = addResource("Resource Marta", "marta", true, "ScopeA", "ScopeB", "ScopeC");
permission.setName(resourceA.getName() + " Permission");
- permission.addResource(resourceA.getName());
+ permission.addResource(resourceA.getId());
permission.addPolicy("Default Policy");
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");
permission.setName(resourceB.getName() + " Permission");
- permission.addResource(resourceB.getName());
+ permission.addResource(resourceB.getId());
permission.addPolicy("Default Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedAccessTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedAccessTest.java
index f8dd3def7c..5ef774efe8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedAccessTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedAccessTest.java
@@ -68,7 +68,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB");
permission.setName(resource.getName() + " Permission");
- permission.addResource(resource.getName());
+ permission.addResource(resource.getId());
permission.addPolicy("Only Owner Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close();
@@ -91,7 +91,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(permissions.isEmpty());
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");
} catch (AuthorizationDeniedException ade) {
@@ -104,7 +104,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB");
permission.setName(resource.getName() + " Permission");
- permission.addResource(resource.getName());
+ permission.addResource(resource.getId());
permission.addPolicy("Only Owner Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close();
@@ -127,7 +127,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(permissions.isEmpty());
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");
} catch (AuthorizationDeniedException ade) {
@@ -156,7 +156,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
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();
assertNotNull(rpt);
@@ -180,7 +180,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
resource = addResource("Resource A", "marta", true);
permission.setName(resource.getName() + " Permission");
- permission.addResource(resource.getName());
+ permission.addResource(resource.getId());
permission.addPolicy("Only Owner Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close();
@@ -203,7 +203,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(permissions.isEmpty());
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");
} catch (AuthorizationDeniedException ade) {
@@ -232,7 +232,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(ticket.isGranted());
}
- response = authorize("kolo", "password", resource.getName(), new String[] {});
+ response = authorize("kolo", "password", resource.getId(), new String[] {});
rpt = response.getToken();
assertNotNull(rpt);
@@ -249,7 +249,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertPermissions(permissions, resource.getName());
assertTrue(permissions.isEmpty());
- response = authorize("kolo", "password", resource.getName(), new String[] {});
+ response = authorize("kolo", "password", resource.getId(), new String[] {});
rpt = response.getToken();
assertNotNull(rpt);
@@ -282,7 +282,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB");
permission.setName(resource.getName() + " Permission");
- permission.addResource(resource.getName());
+ permission.addResource(resource.getId());
permission.addPolicy("Only Owner Policy");
getClient(getRealm()).authorization().permissions().resource().create(permission).close();
@@ -305,7 +305,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
assertTrue(permissions.isEmpty());
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");
} catch (AuthorizationDeniedException ade) {
@@ -324,7 +324,7 @@ public class UserManagedAccessTest extends AbstractResourceServerTest {
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();
assertNotNull(rpt);