[KEYCLOAK-6787] - Wrong validation of resources with same name and different owners
This commit is contained in:
parent
9b1275f182
commit
1e1de85685
30 changed files with 535 additions and 217 deletions
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 <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
|
||||
* @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 <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) {
|
||||
return null;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,11 @@ public interface ResourcesResource {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
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
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
|
|
|
@ -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) {
|
||||
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));
|
||||
|
|
|
@ -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<Resource> 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()) {
|
||||
|
|
|
@ -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))"),
|
||||
|
|
|
@ -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<String> 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();
|
||||
|
|
|
@ -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<Resource> findByType(String type, String resourceServerId) {
|
||||
return delegate.findByType(type, resourceServerId);
|
||||
|
|
|
@ -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())) {
|
||||
|
|
|
@ -97,7 +97,7 @@ public interface ResourceStore {
|
|||
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 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 <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.
|
||||
*
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -118,7 +118,6 @@ public class ResourceSetService {
|
|||
public Response create(ResourceRepresentation resource, Function<Resource, ?> 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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -172,13 +172,13 @@ public class AuthorizationTokenService {
|
|||
|
||||
private List<Result> 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<Result> 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<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();
|
||||
Map<String, Set<String>> permissionsToEvaluate = new LinkedHashMap<>();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
|
@ -294,17 +294,31 @@ public class AuthorizationTokenService {
|
|||
requestedScopes = new HashSet<>();
|
||||
}
|
||||
|
||||
Resource existingResource = null;
|
||||
List<Resource> 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<String> scopes = permissionsToEvaluate.get(existingResource.getId());
|
||||
if (!existingResources.isEmpty()) {
|
||||
for (Resource resource : existingResources) {
|
||||
Set<String> 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<Resource> resources = resourceStore.findByScope(new ArrayList<>(requestedScopes), ticket.getAudience()[0]);
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ public class KeycloakIdentity implements Identity {
|
|||
return this.accessToken;
|
||||
}
|
||||
|
||||
private boolean isResourceServer() {
|
||||
public boolean isResourceServer() {
|
||||
UserModel clientUser = null;
|
||||
|
||||
ClientModel clientModel = getTargetClient();
|
||||
|
|
|
@ -67,41 +67,61 @@ public class AbstractPermissionService {
|
|||
|
||||
private List<ResourceRepresentation> verifyRequestedResource(List<PermissionRequest> request) {
|
||||
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
|
||||
List<ResourceRepresentation> requestedResources = new ArrayList<>();
|
||||
|
||||
return request.stream().map(permissionRequest -> {
|
||||
for (PermissionRequest permissionRequest : request) {
|
||||
String resourceSetId = permissionRequest.getResourceId();
|
||||
Resource resource = null;
|
||||
List<Resource> 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<ScopeRepresentation> 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<ScopeRepresentation> 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<ScopeRepresentation> verifyRequestedScopes(PermissionRequest request, Resource resource) {
|
||||
|
|
|
@ -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 <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";
|
||||
|
||||
@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<RealmRepresentation> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<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();
|
||||
|
||||
assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING.name(), settings.getPolicyEnforcementMode().name());
|
||||
assertEquals(resourceServer.getId(), settings.getClientId());
|
||||
List<ResourceRepresentation> defaultResources = clientResource.authorization().resources().resources();
|
||||
defaultResources = clientResource.authorization().resources().resources();
|
||||
|
||||
assertEquals(1, defaultResources.size());
|
||||
|
||||
List<PolicyRepresentation> 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());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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<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
|
||||
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<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
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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")));
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue