[KEYCLOAK-10713] - Pagination to resources rest api
This commit is contained in:
parent
fe0d6f4583
commit
967d21dbb5
14 changed files with 499 additions and 92 deletions
|
@ -148,6 +148,7 @@ public class StoreFactoryCacheManager extends CacheManager {
|
|||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByGranted(requester, serverId));
|
||||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByGranted(requester, null));
|
||||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByResourceNameAndGranted(resourceName, requester, serverId));
|
||||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByResourceNameAndGranted(resourceName, requester, null));
|
||||
if (scope != null) {
|
||||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByScope(scope, serverId));
|
||||
}
|
||||
|
|
|
@ -676,6 +676,11 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByOwner(String ownerId, String resourceServerId, int first, int max) {
|
||||
return getResourceStoreDelegate().findByOwner(ownerId, resourceServerId, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByUri(String uri, String resourceServerId) {
|
||||
if (uri == null) return null;
|
||||
|
@ -1130,6 +1135,16 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
(revision, permissions) -> new PermissionTicketResourceListQuery(revision, cacheKey, resourceName, permissions.stream().map(permission -> permission.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findGrantedResources(String requester, int first, int max) {
|
||||
return getPermissionTicketStoreDelegate().findGrantedResources(requester, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findGrantedOwnerResources(String owner, int first, int max) {
|
||||
return getPermissionTicketStoreDelegate().findGrantedOwnerResources(owner, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> findByOwner(String owner, String resourceServerId) {
|
||||
String cacheKey = getPermissionTicketByOwner(owner, resourceServerId);
|
||||
|
|
|
@ -41,7 +41,9 @@ import javax.persistence.UniqueConstraint;
|
|||
{
|
||||
@NamedQuery(name="findPermissionIdByResource", query="select p.id from PermissionTicketEntity p inner join p.resource r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)"),
|
||||
@NamedQuery(name="findPermissionIdByScope", query="select p.id from PermissionTicketEntity p inner join p.scope s where p.resourceServer.id = :serverId and (s.resourceServer.id = :serverId and s.id = :scopeId)"),
|
||||
@NamedQuery(name="findPermissionTicketIdByServerId", query="select p.id from PermissionTicketEntity p where p.resourceServer.id = :serverId ")
|
||||
@NamedQuery(name="findPermissionTicketIdByServerId", query="select p.id from PermissionTicketEntity p where p.resourceServer.id = :serverId "),
|
||||
@NamedQuery(name="findGrantedResources", query="select distinct(r.id) from ResourceEntity r inner join PermissionTicketEntity p on r.id = p.resource.id where p.grantedTimestamp is not null and p.requester = :requester order by r.id"),
|
||||
@NamedQuery(name="findGrantedOwnerResources", query="select distinct(r.id) from ResourceEntity r inner join PermissionTicketEntity p on r.id = p.resource.id where p.grantedTimestamp is not null and p.owner = :owner order by r.id")
|
||||
}
|
||||
)
|
||||
public class PermissionTicketEntity {
|
||||
|
|
|
@ -58,7 +58,9 @@ import org.hibernate.annotations.FetchMode;
|
|||
@NamedQueries(
|
||||
{
|
||||
@NamedQuery(name="findResourceIdByOwner", query="select distinct(r) from ResourceEntity r left join fetch r.scopes s where r.resourceServer.id = :serverId and r.owner = :owner"),
|
||||
@NamedQuery(name="findResourceIdByOwnerOrdered", query="select distinct(r) from ResourceEntity r left join fetch r.scopes s where r.resourceServer.id = :serverId and r.owner = :owner order by r.id"),
|
||||
@NamedQuery(name="findAnyResourceIdByOwner", query="select distinct(r) from ResourceEntity r left join fetch r.scopes s where r.owner = :owner"),
|
||||
@NamedQuery(name="findAnyResourceIdByOwnerOrdered", query="select distinct(r) from ResourceEntity r left join fetch r.scopes s where r.owner = :owner order by r.id"),
|
||||
@NamedQuery(name="findResourceIdByUri", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and :uri in elements(r.uris)"),
|
||||
@NamedQuery(name="findResourceIdByName", query="select distinct(r) from ResourceEntity r left join fetch r.scopes s where r.resourceServer.id = :serverId and r.owner = :ownerId and r.name = :name"),
|
||||
@NamedQuery(name="findResourceIdByType", query="select distinct(r) from ResourceEntity r left join fetch r.scopes s where r.resourceServer.id = :serverId and r.owner = :ownerId and r.type = :type"),
|
||||
|
|
|
@ -36,8 +36,10 @@ import javax.persistence.criteria.Root;
|
|||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.jpa.entities.PermissionTicketEntity;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.PermissionTicketStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
/**
|
||||
|
@ -262,6 +264,60 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
|
|||
return find(filters, resourceServerId, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findGrantedResources(String requester, int first, int max) {
|
||||
TypedQuery<String> query = entityManager.createNamedQuery("findGrantedResources", String.class);
|
||||
|
||||
query.setFlushMode(FlushModeType.COMMIT);
|
||||
query.setParameter("requester", requester);
|
||||
|
||||
if (first > -1 && max > -1) {
|
||||
query.setFirstResult(first);
|
||||
query.setMaxResults(max);
|
||||
}
|
||||
|
||||
List<String> result = query.getResultList();
|
||||
List<Resource> list = new LinkedList<>();
|
||||
ResourceStore resourceStore = provider.getStoreFactory().getResourceStore();
|
||||
|
||||
for (String id : result) {
|
||||
Resource resource = resourceStore.findById(id, null);
|
||||
|
||||
if (Objects.nonNull(resource)) {
|
||||
list.add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findGrantedOwnerResources(String owner, int first, int max) {
|
||||
TypedQuery<String> query = entityManager.createNamedQuery("findGrantedOwnerResources", String.class);
|
||||
|
||||
query.setFlushMode(FlushModeType.COMMIT);
|
||||
query.setParameter("owner", owner);
|
||||
|
||||
if (first > -1 && max > -1) {
|
||||
query.setFirstResult(first);
|
||||
query.setMaxResults(max);
|
||||
}
|
||||
|
||||
List<String> result = query.getResultList();
|
||||
List<Resource> list = new LinkedList<>();
|
||||
ResourceStore resourceStore = provider.getStoreFactory().getResourceStore();
|
||||
|
||||
for (String id : result) {
|
||||
Resource resource = resourceStore.findById(id, null);
|
||||
|
||||
if (Objects.nonNull(resource)) {
|
||||
list.add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> findByOwner(String owner, String resourceServerId) {
|
||||
TypedQuery<String> query = entityManager.createNamedQuery("findPolicyIdByType", String.class);
|
||||
|
|
|
@ -110,10 +110,24 @@ public class JPAResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public void findByOwner(String ownerId, String resourceServerId, Consumer<Resource> consumer) {
|
||||
String queryName = "findResourceIdByOwner";
|
||||
findByOwnerFilter(ownerId, resourceServerId, consumer, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByOwner(String ownerId, String resourceServerId, int first, int max) {
|
||||
List<Resource> list = new LinkedList<>();
|
||||
|
||||
findByOwnerFilter(ownerId, resourceServerId, list::add, first, max);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private void findByOwnerFilter(String ownerId, String resourceServerId, Consumer<Resource> consumer, int firstResult, int maxResult) {
|
||||
boolean pagination = firstResult > -1 && maxResult > -1;
|
||||
String queryName = pagination ? "findResourceIdByOwnerOrdered" : "findResourceIdByOwner";
|
||||
|
||||
if (resourceServerId == null) {
|
||||
queryName = "findAnyResourceIdByOwner";
|
||||
queryName = pagination ? "findAnyResourceIdByOwnerOrdered" : "findAnyResourceIdByOwner";
|
||||
}
|
||||
|
||||
TypedQuery<ResourceEntity> query = entityManager.createNamedQuery(queryName, ResourceEntity.class);
|
||||
|
@ -125,11 +139,21 @@ public class JPAResourceStore implements ResourceStore {
|
|||
query.setParameter("serverId", resourceServerId);
|
||||
}
|
||||
|
||||
StoreFactory storeFactory = provider.getStoreFactory();
|
||||
if (pagination) {
|
||||
query.setFirstResult(firstResult);
|
||||
query.setMaxResults(maxResult);
|
||||
}
|
||||
|
||||
query.getResultList().stream()
|
||||
.map(id -> new ResourceAdapter(id, entityManager, storeFactory))
|
||||
.forEach(consumer);
|
||||
ResourceStore resourceStore = provider.getStoreFactory().getResourceStore();
|
||||
List<ResourceEntity> result = query.getResultList();
|
||||
|
||||
for (ResourceEntity entity : result) {
|
||||
Resource cached = resourceStore.findById(entity.getId(), resourceServerId);
|
||||
|
||||
if (cached != null) {
|
||||
consumer.accept(cached);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -491,6 +491,11 @@ public final class AuthorizationProvider implements Provider {
|
|||
delegate.findByOwner(ownerId, resourceServerId, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByOwner(String ownerId, String resourceServerId, int first, int max) {
|
||||
return delegate.findByOwner(ownerId, resourceServerId, first, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByUri(String uri, String resourceServerId) {
|
||||
return delegate.findByUri(uri, resourceServerId);
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
|
||||
/**
|
||||
|
@ -109,4 +110,24 @@ public interface PermissionTicketStore {
|
|||
* @return a list of permissions granted for a particular user
|
||||
*/
|
||||
List<PermissionTicket> findGranted(String resourceName, String userId, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Resource} granted to the given {@code requester}
|
||||
*
|
||||
* @param requester the requester
|
||||
* @param first first result
|
||||
* @param max max result
|
||||
* @return a list of {@link Resource} granted to the given {@code requester}
|
||||
*/
|
||||
List<Resource> findGrantedResources(String requester, int first, int max);
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Resource} granted by the owner to other users
|
||||
*
|
||||
* @param owner the owner
|
||||
* @param first first result
|
||||
* @param max max result
|
||||
* @return a list of {@link Resource} granted by the owner
|
||||
*/
|
||||
List<Resource> findGrantedOwnerResources(String owner, int first, int max);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,8 @@ public interface ResourceStore {
|
|||
|
||||
void findByOwner(String ownerId, String resourceServerId, Consumer<Resource> consumer);
|
||||
|
||||
List<Resource> findByOwner(String ownerId, String resourceServerId, int first, int max);
|
||||
|
||||
/**
|
||||
* Finds all {@link Resource} instances with the given uri.
|
||||
*
|
||||
|
|
|
@ -46,9 +46,13 @@ import java.io.StringWriter;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
|
||||
|
@ -316,7 +320,24 @@ public class SimpleHttp {
|
|||
|
||||
public String getFirstHeader(String name) throws IOException {
|
||||
readResponse();
|
||||
return response.getHeaders(name)[0].getValue();
|
||||
Header[] headers = response.getHeaders(name);
|
||||
|
||||
if (headers != null && headers.length > 0) {
|
||||
return headers[0].getValue();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<String> getHeader(String name) throws IOException {
|
||||
readResponse();
|
||||
Header[] headers = response.getHeaders(name);
|
||||
|
||||
if (headers != null && headers.length > 0) {
|
||||
return Stream.of(headers).map(Header::getValue).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
|
|
|
@ -70,29 +70,6 @@ public abstract class AbstractResourceService {
|
|||
return Cors.add(request, response).auth().allowedOrigins(auth.getToken()).build();
|
||||
}
|
||||
|
||||
protected Collection<ResourcePermission> getPermissions(List<PermissionTicket> tickets, boolean withRequesters) {
|
||||
Map<String, ResourcePermission> permissions = new HashMap<>();
|
||||
|
||||
for (PermissionTicket ticket : tickets) {
|
||||
ResourcePermission resource = permissions
|
||||
.computeIfAbsent(ticket.getResource().getId(), s -> new ResourcePermission(ticket, provider));
|
||||
|
||||
if (withRequesters) {
|
||||
Permission user = resource.getPermission(ticket.getRequester());
|
||||
|
||||
if (user == null) {
|
||||
resource.addPermission(ticket.getRequester(), user = new Permission(ticket.getRequester(), provider));
|
||||
}
|
||||
|
||||
user.addScope(ticket.getScope().getName());
|
||||
} else {
|
||||
resource.addScope(new Scope(ticket.getScope()));
|
||||
}
|
||||
}
|
||||
|
||||
return permissions.values();
|
||||
}
|
||||
|
||||
public static class Resource extends ResourceRepresentation {
|
||||
|
||||
private Client client;
|
||||
|
@ -135,6 +112,11 @@ public abstract class AbstractResourceService {
|
|||
setScopes(new HashSet<>());
|
||||
}
|
||||
|
||||
ResourcePermission(org.keycloak.authorization.model.Resource resource, AuthorizationProvider provider) {
|
||||
super(resource, provider);
|
||||
setScopes(new HashSet<>());
|
||||
}
|
||||
|
||||
public Collection<Permission> getPermissions() {
|
||||
if (permissions == null) {
|
||||
return null;
|
||||
|
|
|
@ -74,14 +74,14 @@ public class ResourceService extends AbstractResourceService {
|
|||
@GET
|
||||
@Path("permissions")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getPermissions() {
|
||||
public Response toPermissions() {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
|
||||
filters.put(PermissionTicket.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
|
||||
Collection<ResourcePermission> resources = getPermissions(ticketStore.find(filters, null, -1, -1), true);
|
||||
Collection<ResourcePermission> resources = toPermissions(ticketStore.find(filters, null, -1, -1));
|
||||
Collection<Permission> permissions = Collections.EMPTY_LIST;
|
||||
|
||||
if (!resources.isEmpty()) {
|
||||
|
@ -210,4 +210,23 @@ public class ResourceService extends AbstractResourceService {
|
|||
|
||||
return user;
|
||||
}
|
||||
|
||||
private Collection<ResourcePermission> toPermissions(List<PermissionTicket> tickets) {
|
||||
Map<String, ResourcePermission> permissions = new HashMap<>();
|
||||
|
||||
for (PermissionTicket ticket : tickets) {
|
||||
ResourcePermission resource = permissions
|
||||
.computeIfAbsent(ticket.getResource().getId(), s -> new ResourcePermission(ticket, provider));
|
||||
|
||||
Permission user = resource.getPermission(ticket.getRequester());
|
||||
|
||||
if (user == null) {
|
||||
resource.addPermission(ticket.getRequester(), user = new Permission(ticket.getRequester(), provider));
|
||||
}
|
||||
|
||||
user.addScope(ticket.getScope().getName());
|
||||
}
|
||||
|
||||
return permissions.values();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,13 +22,23 @@ import javax.ws.rs.NotFoundException;
|
|||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Link;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.store.PermissionTicketStore;
|
||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.managers.Auth;
|
||||
|
@ -46,45 +56,48 @@ public class ResourcesService extends AbstractResourceService {
|
|||
/**
|
||||
* Returns a list of {@link Resource} where the {@link #user} is the resource owner.
|
||||
*
|
||||
* @param first the first result
|
||||
* @param max the max result
|
||||
* @return a list of {@link Resource} where the {@link #user} is the resource owner
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getResources() {
|
||||
return cors(Response.ok(resourceStore.findByOwner(user.getId(), null).stream()
|
||||
.map(resource -> new Resource(resource, user, provider))
|
||||
.collect(Collectors.toList())));
|
||||
public Response getResources(@QueryParam("first") Integer first, @QueryParam("max") Integer max) {
|
||||
return queryResponse((f, m) -> resourceStore.findByOwner(user.getId(), null, f, m)
|
||||
.stream().map(resource -> new Resource(resource, user, provider)), first, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Resource} shared with the {@link #user}
|
||||
*
|
||||
* @param first the first result
|
||||
* @param max the max result
|
||||
* @return a list of {@link Resource} shared with the {@link #user}
|
||||
*/
|
||||
@GET
|
||||
@Path("shared-with-me")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getSharedWithMe() {
|
||||
return cors(Response.ok(getPermissions(ticketStore.findGranted(user.getId(), null), false)));
|
||||
public Response getSharedWithMe(@QueryParam("first") Integer first, @QueryParam("max") Integer max) {
|
||||
return queryResponse((f, m) -> toPermissions(ticketStore.findGrantedResources(auth.getUser().getId(), f, m), false)
|
||||
.stream(), first, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Resource} where the {@link #user} is the resource owner and the resource is
|
||||
* shared with other users.
|
||||
*
|
||||
* @param first the first result
|
||||
* @param max the max result
|
||||
* @return a list of {@link Resource} where the {@link #user} is the resource owner and the resource is
|
||||
* * shared with other users
|
||||
*/
|
||||
@GET
|
||||
@Path("shared-with-others")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getSharedWithOthers() {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
|
||||
filters.put(PermissionTicket.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
|
||||
return cors(Response.ok(getPermissions(ticketStore.find(filters, null, -1, -1), true)));
|
||||
public Response getSharedWithOthers(@QueryParam("first") Integer first, @QueryParam("max") Integer max) {
|
||||
return queryResponse(
|
||||
(f, m) -> toPermissions(ticketStore.findGrantedOwnerResources(auth.getUser().getId(), f, m), true)
|
||||
.stream(), first, max);
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
|
@ -101,4 +114,86 @@ public class ResourcesService extends AbstractResourceService {
|
|||
|
||||
return new ResourceService(resource, provider.getKeycloakSession(), user, auth, request);
|
||||
}
|
||||
|
||||
private Collection<ResourcePermission> toPermissions(List<org.keycloak.authorization.model.Resource> resources, boolean withRequesters) {
|
||||
Collection<ResourcePermission> permissions = new ArrayList<>();
|
||||
PermissionTicketStore ticketStore = provider.getStoreFactory().getPermissionTicketStore();
|
||||
|
||||
for (org.keycloak.authorization.model.Resource resource : resources) {
|
||||
ResourcePermission permission = new ResourcePermission(resource, provider);
|
||||
|
||||
List<PermissionTicket> tickets;
|
||||
|
||||
if (withRequesters) {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
|
||||
filters.put(PermissionTicket.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
|
||||
tickets = ticketStore.find(filters, null, -1, -1);
|
||||
} else {
|
||||
tickets = ticketStore.findGranted(resource.getName(), user.getId(), null);
|
||||
}
|
||||
|
||||
for (PermissionTicket ticket : tickets) {
|
||||
if (resource.equals(ticket.getResource())) {
|
||||
if (withRequesters) {
|
||||
Permission user = permission.getPermission(ticket.getRequester());
|
||||
|
||||
if (user == null) {
|
||||
permission.addPermission(ticket.getRequester(),
|
||||
user = new Permission(ticket.getRequester(), provider));
|
||||
}
|
||||
|
||||
user.addScope(ticket.getScope().getName());
|
||||
} else {
|
||||
permission.addScope(new Scope(ticket.getScope()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
permissions.add(permission);
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
private Response queryResponse(BiFunction<Integer, Integer, Stream<?>> query, Integer first, Integer max) {
|
||||
if (first != null && max != null) {
|
||||
List result = query.apply(first, max + 1).collect(Collectors.toList());
|
||||
int size = result.size();
|
||||
|
||||
if (size > max) {
|
||||
result = result.subList(0, size - 1);
|
||||
}
|
||||
|
||||
return cors(Response.ok().entity(result).links(createPageLinks(first, max, size)));
|
||||
}
|
||||
|
||||
return cors(Response.ok().entity(query.apply(-1, -1).collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
private Link[] createPageLinks(Integer first, Integer max, int resultSize) {
|
||||
if (resultSize == 0 || (first == 0 && resultSize <= max)) {
|
||||
return new Link[] {};
|
||||
}
|
||||
|
||||
List<Link> links = new ArrayList();
|
||||
boolean nextPage = resultSize > max;
|
||||
|
||||
if (nextPage) {
|
||||
links.add(Link.fromUri(
|
||||
KeycloakUriBuilder.fromUri(request.getUri().getRequestUri()).replaceQuery("first={first}&max={max}")
|
||||
.build(first + max, max))
|
||||
.rel("next").build());
|
||||
}
|
||||
|
||||
links.add(Link.fromUri(
|
||||
KeycloakUriBuilder.fromUri(request.getUri().getRequestUri()).replaceQuery("first={first}&max={max}")
|
||||
.build(nextPage ? first : first - max, max))
|
||||
.rel("prev").build());
|
||||
|
||||
return links.toArray(new Link[links.size()]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@ package org.keycloak.testsuite.account;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
|
@ -26,9 +28,11 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.junit.Test;
|
||||
|
@ -42,7 +46,6 @@ import org.keycloak.jose.jws.JWSInput;
|
|||
import org.keycloak.models.AccountRoles;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
@ -152,21 +155,45 @@ public class ResourcesRestServiceTest extends AbstractRestServiceTest {
|
|||
List<Resource> resources = getMyResources();
|
||||
|
||||
assertEquals(30, resources.size());
|
||||
|
||||
for (int i = 0; i < 30; i++) {
|
||||
String uri = "http://resourceServer.com/resources/" + i;
|
||||
Resource resource = resources.stream()
|
||||
.filter(rep -> rep.getUris().stream().anyMatch(resourceUri -> resourceUri.equals(uri))).findAny()
|
||||
.get();
|
||||
|
||||
assertNotNull(resource.getId());
|
||||
assertEquals("Resource " + i, resource.getName());
|
||||
assertEquals("Display Name " + i, resource.getDisplayName());
|
||||
assertEquals("Icon Uri " + i, resource.getIconUri());
|
||||
assertEquals("my-resource-server", resource.getClient().getClientId());
|
||||
assertEquals("My Resource Server", resource.getClient().getName());
|
||||
assertEquals("http://resourceserver.com", resource.getClient().getBaseUrl());
|
||||
assertMyResourcesResponse(resources);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMyResourcesPagination() {
|
||||
List<Resource> resources = getMyResources(0, 10, response -> assertNextPageLink(response, "/realms/test/account/resources", 10, 10));
|
||||
|
||||
assertEquals(10, resources.size());
|
||||
assertMyResourcesResponse(resources);
|
||||
|
||||
resources = getMyResources(10, 10, response -> assertNextPageLink(response, "/realms/test/account/resources", 20, 10));
|
||||
|
||||
assertEquals(10, resources.size());
|
||||
|
||||
resources = getMyResources(20, 10, response -> {
|
||||
assertNextPageLink(response, "/realms/test/account/resources", 20, 10, true);
|
||||
});
|
||||
|
||||
assertEquals(10, resources.size());
|
||||
|
||||
resources = getMyResources(30, 10);
|
||||
|
||||
assertEquals(0, resources.size());
|
||||
|
||||
getMyResources(30, 30, response -> {
|
||||
assertNextPageLink(response, "/realms/test/account/resources", 0, 0, true, true);
|
||||
});
|
||||
|
||||
getMyResources(30, 31, response -> {
|
||||
assertNextPageLink(response, "/realms/test/account/resources", 0, 0, true, true);
|
||||
});
|
||||
|
||||
getMyResources(0, 30, response -> {
|
||||
assertNextPageLink(response, "/realms/test/account/resources", 0, 0, true, true);
|
||||
});
|
||||
|
||||
getMyResources(0, 31, response -> {
|
||||
assertNextPageLink(response, "/realms/test/account/resources", 0, 0, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -175,20 +202,25 @@ public class ResourcesRestServiceTest extends AbstractRestServiceTest {
|
|||
List<AbstractResourceService.ResourcePermission> resources = getSharedWithMe(userName);
|
||||
|
||||
assertEquals(10, resources.size());
|
||||
|
||||
for (AbstractResourceService.ResourcePermission resource : resources) {
|
||||
String uri = resource.getUri();
|
||||
int id = Integer.parseInt(uri.substring(uri.lastIndexOf('/') + 1));
|
||||
assertNotNull(resource.getId());
|
||||
assertEquals("Resource " + id, resource.getName());
|
||||
assertEquals("Display Name " + id, resource.getDisplayName());
|
||||
assertEquals("Icon Uri " + id, resource.getIconUri());
|
||||
assertEquals("my-resource-server", resource.getClient().getClientId());
|
||||
assertEquals("My Resource Server", resource.getClient().getName());
|
||||
assertEquals("http://resourceserver.com", resource.getClient().getBaseUrl());
|
||||
assertEquals(2, resource.getScopes().size());
|
||||
assertSharedWithMeResponse(resources);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSharedWithMePagination() {
|
||||
for (String userName : userNames) {
|
||||
List<AbstractResourceService.ResourcePermission> resources = getSharedWithMe(userName, 0, 3,
|
||||
response -> assertNextPageLink(response, "/realms/test/account/resources/shared-with-me", 3, 3));
|
||||
|
||||
assertSharedWithMeResponse(resources);
|
||||
|
||||
getSharedWithMe(userName, 3, 3,
|
||||
response -> assertNextPageLink(response, "/realms/test/account/resources/shared-with-me", 6, 3));
|
||||
getSharedWithMe(userName, 6, 3,
|
||||
response -> assertNextPageLink(response, "/realms/test/account/resources/shared-with-me", 9, 3));
|
||||
getSharedWithMe(userName, 9, 3,
|
||||
response -> assertNextPageLink(response, "/realms/test/account/resources/shared-with-me", 9, 3, true));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -198,24 +230,27 @@ public class ResourcesRestServiceTest extends AbstractRestServiceTest {
|
|||
});
|
||||
|
||||
assertEquals(30, resources.size());
|
||||
|
||||
for (AbstractResourceService.ResourcePermission resource : resources) {
|
||||
String uri = resource.getUri();
|
||||
int id = Integer.parseInt(uri.substring(uri.lastIndexOf('/') + 1));
|
||||
assertNotNull(resource.getId());
|
||||
assertEquals("Resource " + id, resource.getName());
|
||||
assertEquals("Display Name " + id, resource.getDisplayName());
|
||||
assertEquals("Icon Uri " + id, resource.getIconUri());
|
||||
assertEquals("my-resource-server", resource.getClient().getClientId());
|
||||
assertEquals("My Resource Server", resource.getClient().getName());
|
||||
assertEquals("http://resourceserver.com", resource.getClient().getBaseUrl());
|
||||
assertEquals(1, resource.getPermissions().size());
|
||||
Permission user = resource.getPermissions().iterator().next();
|
||||
|
||||
assertTrue(userNames.contains(user.getUsername()));
|
||||
|
||||
assertEquals(2, user.getScopes().size());
|
||||
assertSharedWithOthersResponse(resources);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSharedWithOthersPagination() {
|
||||
List<AbstractResourceService.ResourcePermission> resources = doGet("/shared-with-others?first=0&max=5",
|
||||
new TypeReference<List<AbstractResourceService.ResourcePermission>>() {
|
||||
}, response -> assertNextPageLink(response, "/realms/test/account/resources/shared-with-others", 5, 5));
|
||||
|
||||
assertEquals(5, resources.size());
|
||||
assertSharedWithOthersResponse(resources);
|
||||
|
||||
doGet("/shared-with-others?first=5&max=5",
|
||||
new TypeReference<List<AbstractResourceService.ResourcePermission>>() {
|
||||
}, response -> assertNextPageLink(response, "/realms/test/account/resources/shared-with-others", 10, 5));
|
||||
doGet("/shared-with-others?first=20&max=5",
|
||||
new TypeReference<List<AbstractResourceService.ResourcePermission>>() {
|
||||
}, response -> assertNextPageLink(response, "/realms/test/account/resources/shared-with-others", 25, 5));
|
||||
doGet("/shared-with-others?first=25&max=5",
|
||||
new TypeReference<List<AbstractResourceService.ResourcePermission>>() {
|
||||
}, response -> assertNextPageLink(response, "/realms/test/account/resources/shared-with-others", 25, 5, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -287,7 +322,7 @@ public class ResourcesRestServiceTest extends AbstractRestServiceTest {
|
|||
|
||||
@Test
|
||||
public void testShareResource() throws Exception {
|
||||
List<String> users = Arrays.asList("jdoe", "alice");
|
||||
List<String> users = new LinkedList<>(Arrays.asList("jdoe", "alice"));
|
||||
List<Permission> permissions = new ArrayList<>();
|
||||
AbstractResourceService.ResourcePermission sharedResource = null;
|
||||
|
||||
|
@ -514,14 +549,26 @@ public class ResourcesRestServiceTest extends AbstractRestServiceTest {
|
|||
}
|
||||
|
||||
private List<AbstractResourceService.ResourcePermission> getSharedWithMe(String userName) {
|
||||
return getSharedWithMe(userName, -1, -1, null);
|
||||
}
|
||||
|
||||
private List<AbstractResourceService.ResourcePermission> getSharedWithMe(String userName, int first, int max, Consumer<SimpleHttp.Response> responseHandler) {
|
||||
if (first > -1 && max > -1) {
|
||||
return doGet("/shared-with-me?first=" + first + "&max=" + max, authzClient.obtainAccessToken(userName, "password").getToken(),
|
||||
new TypeReference<List<AbstractResourceService.ResourcePermission>>() {}, responseHandler);
|
||||
}
|
||||
return doGet("/shared-with-me", authzClient.obtainAccessToken(userName, "password").getToken(),
|
||||
new TypeReference<List<AbstractResourceService.ResourcePermission>>() {});
|
||||
new TypeReference<List<AbstractResourceService.ResourcePermission>>() {}, responseHandler);
|
||||
}
|
||||
|
||||
private <R> R doGet(String resource, TypeReference<R> typeReference) {
|
||||
return doGet(resource, tokenUtil.getToken(), typeReference);
|
||||
}
|
||||
|
||||
private <R> R doGet(String resource, TypeReference<R> typeReference, Consumer<SimpleHttp.Response> response) {
|
||||
return doGet(resource, tokenUtil.getToken(), typeReference, response);
|
||||
}
|
||||
|
||||
private <R> R doGet(String resource, Class<R> type) {
|
||||
return doGet(resource, tokenUtil.getToken(), type);
|
||||
}
|
||||
|
@ -534,6 +581,25 @@ public class ResourcesRestServiceTest extends AbstractRestServiceTest {
|
|||
}
|
||||
}
|
||||
|
||||
private <R> R doGet(String resource, String token, TypeReference<R> typeReference, Consumer<SimpleHttp.Response> responseHandler) {
|
||||
try {
|
||||
SimpleHttp http = get(resource, token);
|
||||
|
||||
http.header("Accept", "application/json");
|
||||
SimpleHttp.Response response = http.asResponse();
|
||||
|
||||
if (responseHandler != null) {
|
||||
responseHandler.accept(response);
|
||||
}
|
||||
|
||||
R result = JsonSerialization.readValue(response.asString(), typeReference);
|
||||
|
||||
return result;
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to fetch resource", cause);
|
||||
}
|
||||
}
|
||||
|
||||
private <R> R doGet(String resource, String token, Class<R> type) {
|
||||
try {
|
||||
return get(resource, token).asJson(type);
|
||||
|
@ -567,6 +633,102 @@ public class ResourcesRestServiceTest extends AbstractRestServiceTest {
|
|||
}
|
||||
|
||||
private List<Resource> getMyResources() {
|
||||
return getMyResources(-1, -1);
|
||||
}
|
||||
|
||||
private List<Resource> getMyResources(int first, int max) {
|
||||
if (first > -1 && max > -1) {
|
||||
return doGet("?first=" + first + "&max=" + max, new TypeReference<List<Resource>>() {});
|
||||
}
|
||||
return doGet("", new TypeReference<List<Resource>>() {});
|
||||
}
|
||||
|
||||
private List<Resource> getMyResources(int first, int max, Consumer<SimpleHttp.Response> response) {
|
||||
if (first > -1 && max > -1) {
|
||||
return doGet("?first=" + first + "&max=" + max, new TypeReference<List<Resource>>() {}, response);
|
||||
}
|
||||
return doGet("", new TypeReference<List<Resource>>() {}, response);
|
||||
}
|
||||
|
||||
private void assertSharedWithOthersResponse(List<AbstractResourceService.ResourcePermission> resources) {
|
||||
for (AbstractResourceService.ResourcePermission resource : resources) {
|
||||
String uri = resource.getUri();
|
||||
int id = Integer.parseInt(uri.substring(uri.lastIndexOf('/') + 1));
|
||||
assertNotNull(resource.getId());
|
||||
assertEquals("Resource " + id, resource.getName());
|
||||
assertEquals("Display Name " + id, resource.getDisplayName());
|
||||
assertEquals("Icon Uri " + id, resource.getIconUri());
|
||||
assertEquals("my-resource-server", resource.getClient().getClientId());
|
||||
assertEquals("My Resource Server", resource.getClient().getName());
|
||||
assertEquals("http://resourceserver.com", resource.getClient().getBaseUrl());
|
||||
assertEquals(1, resource.getPermissions().size());
|
||||
Permission user = resource.getPermissions().iterator().next();
|
||||
|
||||
assertTrue(userNames.contains(user.getUsername()));
|
||||
|
||||
assertEquals(2, user.getScopes().size());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMyResourcesResponse(List<Resource> resources) {
|
||||
for (Resource resource : resources) {
|
||||
String uri = resource.getUri();
|
||||
int id = Integer.parseInt(uri.substring(uri.lastIndexOf('/') + 1));
|
||||
|
||||
assertNotNull(resource.getId());
|
||||
assertEquals("Resource " + id, resource.getName());
|
||||
assertEquals("Display Name " + id, resource.getDisplayName());
|
||||
assertEquals("Icon Uri " + id, resource.getIconUri());
|
||||
assertEquals("my-resource-server", resource.getClient().getClientId());
|
||||
assertEquals("My Resource Server", resource.getClient().getName());
|
||||
assertEquals("http://resourceserver.com", resource.getClient().getBaseUrl());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertSharedWithMeResponse(List<AbstractResourceService.ResourcePermission> resources) {
|
||||
for (AbstractResourceService.ResourcePermission resource : resources) {
|
||||
String uri = resource.getUri();
|
||||
int id = Integer.parseInt(uri.substring(uri.lastIndexOf('/') + 1));
|
||||
assertNotNull(resource.getId());
|
||||
assertEquals("Resource " + id, resource.getName());
|
||||
assertEquals("Display Name " + id, resource.getDisplayName());
|
||||
assertEquals("Icon Uri " + id, resource.getIconUri());
|
||||
assertEquals("my-resource-server", resource.getClient().getClientId());
|
||||
assertEquals("My Resource Server", resource.getClient().getName());
|
||||
assertEquals("http://resourceserver.com", resource.getClient().getBaseUrl());
|
||||
assertEquals(2, resource.getScopes().size());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertNextPageLink(SimpleHttp.Response response, String uri, int first, int max) {
|
||||
assertNextPageLink(response, uri, first, max, false);
|
||||
}
|
||||
|
||||
private void assertNextPageLink(SimpleHttp.Response response, String uri, int first, int max, boolean lastPage) {
|
||||
assertNextPageLink(response, uri, first, max, lastPage, false);
|
||||
}
|
||||
|
||||
private void assertNextPageLink(SimpleHttp.Response response, String uri, int first, int max, boolean lastPage, boolean singlePage) {
|
||||
try {
|
||||
List<String> links = response.getHeader("Link");
|
||||
|
||||
if (singlePage) {
|
||||
assertNull(links);
|
||||
return;
|
||||
}
|
||||
|
||||
assertNotNull(links);
|
||||
assertEquals(lastPage ? 1 : 2, links.size());
|
||||
|
||||
for (String link : links) {
|
||||
if (link.contains("rel=\"next\"")) {
|
||||
assertEquals("<" + authzClient.getConfiguration().getAuthServerUrl() + uri + "?first=" + first + "&max=" + max + ">; rel=\"next\"", link);
|
||||
} else {
|
||||
assertEquals("<" + authzClient.getConfiguration().getAuthServerUrl() + uri + "?first=" + (first - max) + "&max=" + max + ">; rel=\"prev\"", link);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
fail("Fail to get link header");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue