[KEYCLOAK-8060] - My Resources REST API
This commit is contained in:
parent
1463539d32
commit
fdc0943a92
12 changed files with 1319 additions and 117 deletions
|
@ -104,13 +104,13 @@ public class PermissionResource {
|
|||
if (ticket == null) {
|
||||
throw new IllegalArgumentException("Permission ticket must not be null or empty");
|
||||
}
|
||||
if (ticket.getRequester() == null || ticket.getRequesterName() == null) {
|
||||
if (ticket.getRequester() == null && ticket.getRequesterName() == null) {
|
||||
throw new IllegalArgumentException("Permission ticket must have a requester");
|
||||
}
|
||||
if (ticket.getResource() == null || ticket.getResourceName() == null) {
|
||||
if (ticket.getResource() == null && ticket.getResourceName() == null) {
|
||||
throw new IllegalArgumentException("Permission ticket must have a resource");
|
||||
}
|
||||
if (ticket.getScope() == null || ticket.getScopeName() == null) {
|
||||
if (ticket.getScope() == null && ticket.getScopeName() == null) {
|
||||
throw new IllegalArgumentException("Permission ticket must have a scope");
|
||||
}
|
||||
Callable<PermissionTicketRepresentation> callable = new Callable<PermissionTicketRepresentation>() {
|
||||
|
|
|
@ -266,6 +266,13 @@ public class ResourceRepresentation {
|
|||
}
|
||||
}
|
||||
|
||||
public void addScope(ScopeRepresentation scope) {
|
||||
if (scopes == null) {
|
||||
scopes = new HashSet<>();
|
||||
}
|
||||
scopes.add(scope);
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
|
|
@ -146,6 +146,7 @@ public class StoreFactoryCacheManager extends CacheManager {
|
|||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByOwner(owner, serverId));
|
||||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByResource(resource, serverId));
|
||||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByGranted(requester, serverId));
|
||||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByGranted(requester, null));
|
||||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByResourceNameAndGranted(resourceName, requester, serverId));
|
||||
if (scope != null) {
|
||||
invalidations.add(StoreFactoryCacheSession.getPermissionTicketByScope(scope, serverId));
|
||||
|
|
|
@ -27,8 +27,10 @@ import org.apache.http.HttpResponse;
|
|||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
|
@ -97,6 +99,10 @@ public class SimpleHttp {
|
|||
return new SimpleHttp(url, "POST", client);
|
||||
}
|
||||
|
||||
public static SimpleHttp doPut(String url, HttpClient client) {
|
||||
return new SimpleHttp(url, "PUT", client);
|
||||
}
|
||||
|
||||
public SimpleHttp header(String name, String value) {
|
||||
if (headers == null) {
|
||||
headers = new HashMap<String, String>();
|
||||
|
@ -166,9 +172,11 @@ public class SimpleHttp {
|
|||
private Response makeRequest() throws IOException {
|
||||
boolean get = method.equals("GET");
|
||||
boolean post = method.equals("POST");
|
||||
boolean put = method.equals("PUT");
|
||||
boolean delete = method.equals("DELETE");
|
||||
|
||||
HttpRequestBase httpRequest = new HttpPost(url);
|
||||
|
||||
if (get) {
|
||||
httpRequest = new HttpGet(appendParameterToUrl(url));
|
||||
}
|
||||
|
@ -177,14 +185,18 @@ public class SimpleHttp {
|
|||
httpRequest = new HttpDelete(appendParameterToUrl(url));
|
||||
}
|
||||
|
||||
if (post) {
|
||||
if (put) {
|
||||
httpRequest = new HttpPut(appendParameterToUrl(url));
|
||||
}
|
||||
|
||||
if (post || put) {
|
||||
if (params != null) {
|
||||
((HttpPost) httpRequest).setEntity(getFormEntityFromParameter());
|
||||
((HttpEntityEnclosingRequestBase) httpRequest).setEntity(getFormEntityFromParameter());
|
||||
} else if (entity != null) {
|
||||
if (headers == null || !headers.containsKey("Content-Type")) {
|
||||
header("Content-Type", "application/json");
|
||||
}
|
||||
((HttpPost) httpRequest).setEntity(getJsonEntity());
|
||||
((HttpEntityEnclosingRequestBase) httpRequest).setEntity(getJsonEntity());
|
||||
} else {
|
||||
throw new IllegalStateException("No content set");
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.keycloak.forms.login.freemarker.model.UrlBean;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.theme.FreeMarkerUtil;
|
||||
|
@ -64,7 +65,14 @@ public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
|
|||
}
|
||||
|
||||
if (!MediaTypeMatcher.isHtmlRequest(headers)) {
|
||||
return Response.status(statusCode).build();
|
||||
OAuth2ErrorRepresentation error = new OAuth2ErrorRepresentation();
|
||||
|
||||
error.setError(getErrorCode(throwable));
|
||||
|
||||
return Response.status(statusCode)
|
||||
.header(HttpHeaders.CONTENT_TYPE, javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE.toString())
|
||||
.entity(error)
|
||||
.build();
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -100,6 +108,16 @@ public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
|
|||
return status;
|
||||
}
|
||||
|
||||
private String getErrorCode(Throwable throwable) {
|
||||
String error = throwable.getMessage();
|
||||
|
||||
if (error == null) {
|
||||
return "unknown_error";
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
private RealmModel resolveRealm() {
|
||||
String path = session.getContext().getUri().getPath();
|
||||
Matcher m = realmNamePattern.matcher(path);
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.keycloak.services.managers.Auth;
|
|||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.Cors;
|
||||
import org.keycloak.services.resources.account.resources.ResourcesService;
|
||||
import org.keycloak.storage.ReadOnlyException;
|
||||
|
||||
import javax.ws.rs.*;
|
||||
|
@ -288,6 +289,13 @@ public class AccountRestService {
|
|||
return new AccountCredentialResource(session, event, user, auth);
|
||||
}
|
||||
|
||||
@Path("/resources")
|
||||
public ResourcesService resources() {
|
||||
checkAccountApiEnabled();
|
||||
auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
|
||||
return new ResourcesService(session, user, auth, request);
|
||||
}
|
||||
|
||||
// TODO Federated identities
|
||||
// TODO Applications
|
||||
// TODO Logs
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* Copyright 2016 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.services.resources.account.resources;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.PermissionTicketStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.managers.Auth;
|
||||
import org.keycloak.services.resources.Cors;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public abstract class AbstractResourceService {
|
||||
|
||||
protected final UserModel user;
|
||||
protected final AuthorizationProvider provider;
|
||||
protected final PermissionTicketStore ticketStore;
|
||||
protected final ResourceStore resourceStore;
|
||||
protected final ScopeStore scopeStore;
|
||||
protected HttpRequest request;
|
||||
protected Auth auth;
|
||||
|
||||
protected AbstractResourceService(KeycloakSession session, UserModel user, Auth auth, HttpRequest request) {
|
||||
this.user = user;
|
||||
this.auth = auth;
|
||||
this.request = request;
|
||||
provider = session.getProvider(AuthorizationProvider.class);
|
||||
ticketStore = provider.getStoreFactory().getPermissionTicketStore();
|
||||
resourceStore = provider.getStoreFactory().getResourceStore();
|
||||
scopeStore = provider.getStoreFactory().getScopeStore();
|
||||
}
|
||||
|
||||
protected Response cors(Response.ResponseBuilder response) {
|
||||
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;
|
||||
|
||||
public Resource() {
|
||||
}
|
||||
|
||||
Resource(org.keycloak.authorization.model.Resource resource, UserModel owner, AuthorizationProvider provider) {
|
||||
setId(resource.getId());
|
||||
setName(resource.getName());
|
||||
setDisplayName(resource.getDisplayName());
|
||||
setUris(resource.getUris());
|
||||
setIconUri(resource.getIconUri());
|
||||
|
||||
setScopes(resource.getScopes().stream().map(Scope::new).collect(Collectors.toSet()));
|
||||
|
||||
ResourceServer resourceServer = resource.getResourceServer();
|
||||
this.client = new Client(provider.getRealm().getClientById(resourceServer.getId()));
|
||||
}
|
||||
|
||||
Resource(org.keycloak.authorization.model.Resource resource, AuthorizationProvider provider) {
|
||||
this(resource, null, provider);
|
||||
}
|
||||
|
||||
public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ResourcePermission extends Resource {
|
||||
|
||||
private Map<String, Permission> permissions;
|
||||
|
||||
public ResourcePermission() {
|
||||
|
||||
}
|
||||
|
||||
ResourcePermission(PermissionTicket ticket, AuthorizationProvider provider) {
|
||||
super(ticket.getResource(), provider);
|
||||
setScopes(new HashSet<>());
|
||||
}
|
||||
|
||||
public Collection<Permission> getPermissions() {
|
||||
if (permissions == null) {
|
||||
return null;
|
||||
}
|
||||
return permissions.values();
|
||||
}
|
||||
|
||||
public void setPermissions(Collection<Permission> permissions) {
|
||||
for (Permission permission : permissions) {
|
||||
addPermission(permission.getUsername(), permission);
|
||||
}
|
||||
}
|
||||
|
||||
public void addPermission(String requester, Permission permission) {
|
||||
if (permissions == null) {
|
||||
permissions = new HashMap<>();
|
||||
}
|
||||
permissions.put(requester, permission);
|
||||
}
|
||||
|
||||
public Permission getPermission(String requester) {
|
||||
if (permissions == null) {
|
||||
return null;
|
||||
}
|
||||
return permissions.get(requester);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Permission extends UserRepresentation {
|
||||
|
||||
private List<String> scopes;
|
||||
|
||||
public Permission() {
|
||||
|
||||
}
|
||||
|
||||
Permission(String userId, AuthorizationProvider provider) {
|
||||
UserModel user = provider.getKeycloakSession().users().getUserById(userId, provider.getRealm());
|
||||
|
||||
setUsername(user.getUsername());
|
||||
setFirstName(user.getFirstName());
|
||||
setLastName(user.getLastName());
|
||||
setEmail(user.getEmail());
|
||||
}
|
||||
|
||||
Permission(PermissionTicket ticket, AuthorizationProvider provider) {
|
||||
this(ticket.getRequester(), provider);
|
||||
}
|
||||
|
||||
public Permission(String userName, String... scopes) {
|
||||
setUsername(userName);
|
||||
for (String scope : scopes) {
|
||||
addScope(scope);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
public void addScope(String... scope) {
|
||||
if (scopes == null) {
|
||||
scopes = new ArrayList<>();
|
||||
}
|
||||
scopes.addAll(Arrays.asList(scope));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Scope extends ScopeRepresentation {
|
||||
|
||||
public Scope() {
|
||||
|
||||
}
|
||||
|
||||
Scope(org.keycloak.authorization.model.Scope scope) {
|
||||
setName(scope.getName());
|
||||
setDisplayName(scope.getDisplayName());
|
||||
setIconUri(scope.getIconUri());
|
||||
}
|
||||
}
|
||||
|
||||
public static class Client extends ClientRepresentation {
|
||||
|
||||
public Client() {
|
||||
|
||||
}
|
||||
|
||||
Client(ClientModel client) {
|
||||
setClientId(client.getClientId());
|
||||
setName(client.getName());
|
||||
setBaseUrl(client.getBaseUrl());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright 2016 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.services.resources.account.resources;
|
||||
|
||||
import javax.ws.rs.BadRequestException;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.services.managers.Auth;
|
||||
import org.keycloak.utils.MediaType;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ResourceService extends AbstractResourceService {
|
||||
|
||||
private final org.keycloak.authorization.model.Resource resource;
|
||||
private final ResourceServer resourceServer;
|
||||
|
||||
ResourceService(org.keycloak.authorization.model.Resource resource, KeycloakSession session, UserModel user,
|
||||
Auth auth, HttpRequest request) {
|
||||
super(session, user, auth, request);
|
||||
this.resource = resource;
|
||||
this.resourceServer = resource.getResourceServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Resource} where the {@link #user} is the resource owner.
|
||||
*
|
||||
* @return the resource
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getResource() {
|
||||
return cors(Response.ok(new Resource(resource, provider)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Permission} containing the users to which the {@link #user} granted access to a resource.
|
||||
*
|
||||
* @return the users with access to a resource
|
||||
*/
|
||||
@GET
|
||||
@Path("permissions")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getPermissions() {
|
||||
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<Permission> permissions = Collections.EMPTY_LIST;
|
||||
|
||||
if (!resources.isEmpty()) {
|
||||
permissions = resources.iterator().next().getPermissions();
|
||||
}
|
||||
|
||||
return cors(Response.ok(permissions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the permission set for a resource based on the given {@code permissions}.
|
||||
*
|
||||
* @param permissions the permissions that should be updated
|
||||
* @return if successful, a {@link Response.Status#NO_CONTENT} response
|
||||
*/
|
||||
@PUT
|
||||
@Path("permissions")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response revoke(List<Permission> permissions) {
|
||||
if (permissions == null || permissions.isEmpty()) {
|
||||
throw new BadRequestException("invalid_permissions");
|
||||
}
|
||||
|
||||
ResourceServer resourceServer = resource.getResourceServer();
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
|
||||
for (Permission permission : permissions) {
|
||||
UserModel user = getUser(permission.getUsername());
|
||||
|
||||
filters.put(PermissionTicket.REQUESTER, user.getId());
|
||||
|
||||
List<PermissionTicket> tickets = ticketStore.find(filters, resource.getResourceServer().getId(), -1, -1);
|
||||
|
||||
// grants all requested permissions
|
||||
if (tickets.isEmpty()) {
|
||||
for (String scope : permission.getScopes()) {
|
||||
grantPermission(user, scope);
|
||||
}
|
||||
} else {
|
||||
Iterator<String> scopesIterator = permission.getScopes().iterator();
|
||||
|
||||
while (scopesIterator.hasNext()) {
|
||||
org.keycloak.authorization.model.Scope scope = getScope(scopesIterator.next(), resourceServer);
|
||||
Iterator<PermissionTicket> ticketIterator = tickets.iterator();
|
||||
|
||||
while (ticketIterator.hasNext()) {
|
||||
PermissionTicket ticket = ticketIterator.next();
|
||||
|
||||
if (scope.getId().equals(ticket.getScope().getId())) {
|
||||
if (!ticket.isGranted()) {
|
||||
ticket.setGrantedTimestamp(System.currentTimeMillis());
|
||||
}
|
||||
// permission exists, remove from the list to avoid deletion
|
||||
ticketIterator.remove();
|
||||
// scope already granted, remove from the list to avoid creating it again
|
||||
scopesIterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only create permissions for the scopes that don't have a tocket
|
||||
for (String scope : permission.getScopes()) {
|
||||
grantPermission(user, scope);
|
||||
}
|
||||
|
||||
// remove all tickets that are not within the requested permissions
|
||||
for (PermissionTicket ticket : tickets) {
|
||||
ticketStore.delete(ticket.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cors(Response.noContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Permission} requests waiting for the {@link #user} approval.
|
||||
*
|
||||
* @return the permission requests waiting for the user approval
|
||||
*/
|
||||
@GET
|
||||
@Path("permissions/requests")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getPermissionRequests() {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
|
||||
filters.put(PermissionTicket.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
|
||||
Map<String, Permission> requests = new HashMap<>();
|
||||
|
||||
for (PermissionTicket ticket : ticketStore.find(filters, null, -1, -1)) {
|
||||
requests.computeIfAbsent(ticket.getRequester(), requester -> new Permission(ticket, provider)).addScope(ticket.getScope().getName());
|
||||
}
|
||||
|
||||
return cors(Response.ok(requests.values()));
|
||||
}
|
||||
|
||||
private void grantPermission(UserModel user, String scopeId) {
|
||||
org.keycloak.authorization.model.Scope scope = getScope(scopeId, resourceServer);
|
||||
PermissionTicket ticket = ticketStore.create(resource.getId(), scope.getId(), user.getId(), resourceServer);
|
||||
ticket.setGrantedTimestamp(Calendar.getInstance().getTimeInMillis());
|
||||
}
|
||||
|
||||
private org.keycloak.authorization.model.Scope getScope(String scopeId, ResourceServer resourceServer) {
|
||||
org.keycloak.authorization.model.Scope scope = scopeStore.findByName(scopeId, resourceServer.getId());
|
||||
|
||||
if (scope == null) {
|
||||
scope = scopeStore.findById(scopeId, resourceServer.getId());
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
private UserModel getUser(String requester) {
|
||||
UserProvider users = provider.getKeycloakSession().users();
|
||||
UserModel user = users.getUserByUsername(requester, provider.getRealm());
|
||||
|
||||
if (user == null) {
|
||||
user = users.getUserById(requester, provider.getRealm());
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2016 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.services.resources.account.resources;
|
||||
|
||||
import javax.ws.rs.BadRequestException;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.managers.Auth;
|
||||
import org.keycloak.utils.MediaType;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ResourcesService extends AbstractResourceService {
|
||||
|
||||
public ResourcesService(KeycloakSession session, UserModel user, Auth auth, HttpRequest request) {
|
||||
super(session, user, auth, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Resource} where the {@link #user} is the resource owner.
|
||||
*
|
||||
* @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())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Resource} shared with the {@link #user}
|
||||
*
|
||||
* @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)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Resource} where the {@link #user} is the resource owner and the resource is
|
||||
* shared with other users.
|
||||
*
|
||||
* @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)));
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
public Object getResource(@PathParam("id") String id) {
|
||||
org.keycloak.authorization.model.Resource resource = resourceStore.findById(id, null);
|
||||
|
||||
if (resource == null) {
|
||||
throw new NotFoundException("resource_not_found");
|
||||
}
|
||||
|
||||
if (!resource.getOwner().equals(user.getId())) {
|
||||
throw new BadRequestException("invalid_resource");
|
||||
}
|
||||
|
||||
return new ResourceService(resource, provider.getKeycloakSession(), user, auth, request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright 2016 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.account;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.keycloak.common.Profile.Feature.ACCOUNT_API;
|
||||
import static org.keycloak.testsuite.ProfileAssume.assumeFeatureEnabled;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.representations.account.SessionRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.util.TokenUtil;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public abstract class AbstractRestServiceTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
@Rule
|
||||
public TokenUtil tokenUtil = new TokenUtil();
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
protected CloseableHttpClient httpClient;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
httpClient = HttpClientBuilder.create().build();
|
||||
try {
|
||||
checkIfFeatureWorks(false);
|
||||
Response response = testingClient.testing().enableFeature(ACCOUNT_API.toString());
|
||||
assertEquals(200, response.getStatus());
|
||||
assumeFeatureEnabled(ACCOUNT_API);
|
||||
checkIfFeatureWorks(true);
|
||||
} catch (Exception e) {
|
||||
disableFeature();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
try {
|
||||
disableFeature();
|
||||
httpClient.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void disableFeature() {
|
||||
Response response = testingClient.testing().disableFeature(ACCOUNT_API.toString());
|
||||
assertEquals(200, response.getStatus());
|
||||
checkIfFeatureWorks(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
testRealm.getUsers().add(UserBuilder.create().username("no-account-access").password("password").build());
|
||||
testRealm.getUsers().add(UserBuilder.create().username("view-account-access").role("account", "view-profile").password("password").build());
|
||||
}
|
||||
|
||||
protected String getAccountUrl(String resource) {
|
||||
return suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/realms/test/account" + (resource != null ? "/" + resource : "");
|
||||
}
|
||||
|
||||
// Check if the feature really works
|
||||
private void checkIfFeatureWorks(boolean shouldWorks) {
|
||||
try {
|
||||
List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), httpClient).auth(tokenUtil.getToken())
|
||||
.asJson(new TypeReference<List<SessionRepresentation>>() {
|
||||
});
|
||||
assertEquals(1, sessions.size());
|
||||
if (!shouldWorks)
|
||||
fail("Feature is available, but this moment should be disabled");
|
||||
|
||||
} catch (Exception e) {
|
||||
if (shouldWorks) {
|
||||
e.printStackTrace();
|
||||
fail("Feature is not available");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,11 +17,6 @@
|
|||
package org.keycloak.testsuite.account;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.representations.account.SessionRepresentation;
|
||||
|
@ -31,12 +26,8 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
|||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.account.AccountCredentialResource;
|
||||
import org.keycloak.services.resources.account.AccountCredentialResource.PasswordUpdate;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.util.TokenUtil;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -51,57 +42,11 @@ import static org.keycloak.testsuite.ProfileAssume.assumeFeatureEnabled;
|
|||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
@Rule
|
||||
public TokenUtil tokenUtil = new TokenUtil();
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
private CloseableHttpClient client;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
client = HttpClientBuilder.create().build();
|
||||
try {
|
||||
checkIfFeatureWorks(false);
|
||||
Response response = testingClient.testing().enableFeature(ACCOUNT_API.toString());
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
assumeFeatureEnabled(ACCOUNT_API);
|
||||
checkIfFeatureWorks(true);
|
||||
} catch (Exception e) {
|
||||
disableFeature();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
try {
|
||||
disableFeature();
|
||||
client.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void disableFeature() {
|
||||
Response response = testingClient.testing().disableFeature(ACCOUNT_API.toString());
|
||||
assertEquals(200, response.getStatus());
|
||||
checkIfFeatureWorks(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
testRealm.getUsers().add(UserBuilder.create().username("no-account-access").password("password").build());
|
||||
testRealm.getUsers().add(UserBuilder.create().username("view-account-access").role("account", "view-profile").password("password").build());
|
||||
}
|
||||
public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||
|
||||
@Test
|
||||
public void testGetProfile() throws IOException {
|
||||
UserRepresentation user = SimpleHttp.doGet(getAccountUrl(null), client).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
UserRepresentation user = SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
assertEquals("Tom", user.getFirstName());
|
||||
assertEquals("Brady", user.getLastName());
|
||||
assertEquals("test-user@localhost", user.getEmail());
|
||||
|
@ -111,7 +56,7 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testUpdateProfile() throws IOException {
|
||||
UserRepresentation user = SimpleHttp.doGet(getAccountUrl(null), client).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
UserRepresentation user = SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
String originalFirstName = user.getFirstName();
|
||||
String originalLastName = user.getLastName();
|
||||
String originalEmail = user.getEmail();
|
||||
|
@ -182,7 +127,7 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
user.setLastName(originalLastName);
|
||||
user.setEmail(originalEmail);
|
||||
user.setAttributes(originalAttributes);
|
||||
SimpleHttp.Response response = SimpleHttp.doPost(getAccountUrl(null), client).auth(tokenUtil.getToken()).json(user).asResponse();
|
||||
SimpleHttp.Response response = SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).json(user).asResponse();
|
||||
System.out.println(response.asString());
|
||||
assertEquals(200, response.getStatus());
|
||||
}
|
||||
|
@ -196,7 +141,7 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
realmRep.setRegistrationEmailAsUsername(true);
|
||||
adminClient.realm("test").update(realmRep);
|
||||
|
||||
UserRepresentation user = SimpleHttp.doGet(getAccountUrl(null), client).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
UserRepresentation user = SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
String originalFirstname = user.getFirstName();
|
||||
|
||||
try {
|
||||
|
@ -207,20 +152,20 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
assertEquals("Homer1", user.getFirstName());
|
||||
} finally {
|
||||
user.setFirstName(originalFirstname);
|
||||
int status = SimpleHttp.doPost(getAccountUrl(null), client).auth(tokenUtil.getToken()).json(user).asStatus();
|
||||
int status = SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).json(user).asStatus();
|
||||
assertEquals(200, status);
|
||||
}
|
||||
}
|
||||
|
||||
private UserRepresentation updateAndGet(UserRepresentation user) throws IOException {
|
||||
int status = SimpleHttp.doPost(getAccountUrl(null), client).auth(tokenUtil.getToken()).json(user).asStatus();
|
||||
int status = SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).json(user).asStatus();
|
||||
assertEquals(200, status);
|
||||
return SimpleHttp.doGet(getAccountUrl(null), client).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
return SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
}
|
||||
|
||||
|
||||
private void updateError(UserRepresentation user, int expectedStatus, String expectedMessage) throws IOException {
|
||||
SimpleHttp.Response response = SimpleHttp.doPost(getAccountUrl(null), client).auth(tokenUtil.getToken()).json(user).asResponse();
|
||||
SimpleHttp.Response response = SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).json(user).asResponse();
|
||||
assertEquals(expectedStatus, response.getStatus());
|
||||
assertEquals(expectedMessage, response.asJson(ErrorRepresentation.class).getErrorMessage());
|
||||
}
|
||||
|
@ -231,13 +176,13 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
TokenUtil viewToken = new TokenUtil("view-account-access", "password");
|
||||
|
||||
// Read with no access
|
||||
assertEquals(403, SimpleHttp.doGet(getAccountUrl(null), client).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doGet(getAccountUrl(null), httpClient).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
|
||||
// Update with no access
|
||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl(null), client).auth(noaccessToken.getToken()).json(new UserRepresentation()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(noaccessToken.getToken()).json(new UserRepresentation()).asStatus());
|
||||
|
||||
// Update with read only
|
||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl(null), client).auth(viewToken.getToken()).json(new UserRepresentation()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(viewToken.getToken()).json(new UserRepresentation()).asStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -248,38 +193,38 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
TokenUtil viewToken = new TokenUtil("view-account-access", "password");
|
||||
|
||||
// Read sessions with no access
|
||||
assertEquals(403, SimpleHttp.doGet(getAccountUrl("sessions"), client).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doGet(getAccountUrl("sessions"), httpClient).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
|
||||
// Delete all sessions with no access
|
||||
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("sessions"), client).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("sessions"), httpClient).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
|
||||
// Delete all sessions with read only
|
||||
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("sessions"), client).header("Accept", "application/json").auth(viewToken.getToken()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("sessions"), httpClient).header("Accept", "application/json").auth(viewToken.getToken()).asStatus());
|
||||
|
||||
// Delete single session with no access
|
||||
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("session?id=bogusId"), client).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("session?id=bogusId"), httpClient).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
|
||||
// Delete single session with read only
|
||||
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("session?id=bogusId"), client).header("Accept", "application/json").auth(viewToken.getToken()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doDelete(getAccountUrl("session?id=bogusId"), httpClient).header("Accept", "application/json").auth(viewToken.getToken()).asStatus());
|
||||
|
||||
// Read password details with no access
|
||||
assertEquals(403, SimpleHttp.doGet(getAccountUrl("credentials/password"), client).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doGet(getAccountUrl("credentials/password"), httpClient).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus());
|
||||
|
||||
// Update password with no access
|
||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl("credentials/password"), client).auth(noaccessToken.getToken()).json(new PasswordUpdate()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl("credentials/password"), httpClient).auth(noaccessToken.getToken()).json(new PasswordUpdate()).asStatus());
|
||||
|
||||
// Update password with read only
|
||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl("credentials/password"), client).auth(viewToken.getToken()).json(new PasswordUpdate()).asStatus());
|
||||
assertEquals(403, SimpleHttp.doPost(getAccountUrl("credentials/password"), httpClient).auth(viewToken.getToken()).json(new PasswordUpdate()).asStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateProfilePermissions() throws IOException {
|
||||
TokenUtil noaccessToken = new TokenUtil("no-account-access", "password");
|
||||
int status = SimpleHttp.doGet(getAccountUrl(null), client).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus();
|
||||
int status = SimpleHttp.doGet(getAccountUrl(null), httpClient).header("Accept", "application/json").auth(noaccessToken.getToken()).asStatus();
|
||||
assertEquals(403, status);
|
||||
|
||||
TokenUtil viewToken = new TokenUtil("view-account-access", "password");
|
||||
status = SimpleHttp.doGet(getAccountUrl(null), client).header("Accept", "application/json").auth(viewToken.getToken()).asStatus();
|
||||
status = SimpleHttp.doGet(getAccountUrl(null), httpClient).header("Accept", "application/json").auth(viewToken.getToken()).asStatus();
|
||||
assertEquals(200, status);
|
||||
}
|
||||
|
||||
|
@ -287,7 +232,7 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
public void testGetSessions() throws IOException {
|
||||
assumeFeatureEnabled(ACCOUNT_API);
|
||||
|
||||
List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(tokenUtil.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), httpClient).auth(tokenUtil.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
|
||||
assertEquals(1, sessions.size());
|
||||
}
|
||||
|
@ -337,7 +282,7 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
}
|
||||
|
||||
private AccountCredentialResource.PasswordDetails getPasswordDetails() throws IOException {
|
||||
AccountCredentialResource.PasswordDetails details = SimpleHttp.doGet(getAccountUrl("credentials/password"), client).auth(tokenUtil.getToken()).asJson(new TypeReference<AccountCredentialResource.PasswordDetails>() {});
|
||||
AccountCredentialResource.PasswordDetails details = SimpleHttp.doGet(getAccountUrl("credentials/password"), httpClient).auth(tokenUtil.getToken()).asJson(new TypeReference<AccountCredentialResource.PasswordDetails>() {});
|
||||
assertTrue(details.isRegistered());
|
||||
assertNotNull(details.getLastUpdate());
|
||||
return details;
|
||||
|
@ -352,18 +297,18 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
passwordUpdate.setCurrentPassword(currentPass);
|
||||
passwordUpdate.setNewPassword(newPass);
|
||||
passwordUpdate.setConfirmation(confirmation);
|
||||
int status = SimpleHttp.doPost(getAccountUrl("credentials/password"), client).auth(tokenUtil.getToken()).json(passwordUpdate).asStatus();
|
||||
int status = SimpleHttp.doPost(getAccountUrl("credentials/password"), httpClient).auth(tokenUtil.getToken()).json(passwordUpdate).asStatus();
|
||||
assertEquals(expectedStatus, status);
|
||||
}
|
||||
|
||||
public void testDeleteSessions() throws IOException {
|
||||
TokenUtil viewToken = new TokenUtil("view-account-access", "password");
|
||||
oauth.doLogin("view-account-access", "password");
|
||||
List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), httpClient).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
assertEquals(2, sessions.size());
|
||||
int status = SimpleHttp.doDelete(getAccountUrl("sessions?current=false"), client).acceptJson().auth(viewToken.getToken()).asStatus();
|
||||
int status = SimpleHttp.doDelete(getAccountUrl("sessions?current=false"), httpClient).acceptJson().auth(viewToken.getToken()).asStatus();
|
||||
assertEquals(200, status);
|
||||
sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
sessions = SimpleHttp.doGet(getAccountUrl("sessions"), httpClient).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
assertEquals(1, sessions.size());
|
||||
}
|
||||
|
||||
|
@ -373,41 +318,19 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
TokenUtil viewToken = new TokenUtil("view-account-access", "password");
|
||||
String sessionId = oauth.doLogin("view-account-access", "password").getSessionState();
|
||||
List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), httpClient).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
assertEquals(2, sessions.size());
|
||||
|
||||
// With `ViewToken` you can only read
|
||||
int status = SimpleHttp.doDelete(getAccountUrl("session?id=" + sessionId), client).acceptJson().auth(viewToken.getToken()).asStatus();
|
||||
int status = SimpleHttp.doDelete(getAccountUrl("session?id=" + sessionId), httpClient).acceptJson().auth(viewToken.getToken()).asStatus();
|
||||
assertEquals(403, status);
|
||||
sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
sessions = SimpleHttp.doGet(getAccountUrl("sessions"), httpClient).auth(viewToken.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
assertEquals(2, sessions.size());
|
||||
|
||||
// Here you can delete the session
|
||||
status = SimpleHttp.doDelete(getAccountUrl("session?id=" + sessionId), client).acceptJson().auth(tokenUtil.getToken()).asStatus();
|
||||
status = SimpleHttp.doDelete(getAccountUrl("session?id=" + sessionId), httpClient).acceptJson().auth(tokenUtil.getToken()).asStatus();
|
||||
assertEquals(200, status);
|
||||
sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(tokenUtil.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
sessions = SimpleHttp.doGet(getAccountUrl("sessions"), httpClient).auth(tokenUtil.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
assertEquals(1, sessions.size());
|
||||
}
|
||||
|
||||
private String getAccountUrl(String resource) {
|
||||
return suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/realms/test/account" + (resource != null ? "/" + resource : "");
|
||||
}
|
||||
|
||||
// Check if the feature really works
|
||||
private void checkIfFeatureWorks(boolean shouldWorks) {
|
||||
try {
|
||||
List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(tokenUtil.getToken())
|
||||
.asJson(new TypeReference<List<SessionRepresentation>>() {
|
||||
});
|
||||
assertEquals(1, sessions.size());
|
||||
if (!shouldWorks)
|
||||
fail("Feature is available, but this moment should be disabled");
|
||||
|
||||
} catch (Exception e) {
|
||||
if (shouldWorks) {
|
||||
e.printStackTrace();
|
||||
fail("Feature is not available");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,572 @@
|
|||
/*
|
||||
* Copyright 2016 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.account;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
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.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.Configuration;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
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;
|
||||
import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.resources.account.resources.AbstractResourceService;
|
||||
import org.keycloak.services.resources.account.resources.AbstractResourceService.Permission;
|
||||
import org.keycloak.services.resources.account.resources.AbstractResourceService.Resource;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ResourcesRestServiceTest extends AbstractRestServiceTest {
|
||||
|
||||
private AuthzClient authzClient;
|
||||
private List<String> userNames = new ArrayList<>(Arrays.asList("alice", "jdoe", "bob"));
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
super.configureTestRealm(testRealm);
|
||||
RealmRepresentation realmRepresentation = testRealm;
|
||||
|
||||
realmRepresentation.setUserManagedAccessAllowed(true);
|
||||
|
||||
testRealm.getUsers().add(createUser("alice", "password"));
|
||||
testRealm.getUsers().add(createUser("jdoe", "password"));
|
||||
testRealm.getUsers().add(createUser("bob", "password"));
|
||||
|
||||
ClientRepresentation client = ClientBuilder.create()
|
||||
.clientId("my-resource-server")
|
||||
.authorizationServicesEnabled(true)
|
||||
.serviceAccountsEnabled(true)
|
||||
.secret("secret")
|
||||
.name("My Resource Server")
|
||||
.baseUrl("http://resourceserver.com")
|
||||
.directAccessGrants().build();
|
||||
|
||||
testRealm.getClients().add(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void before() {
|
||||
super.before();
|
||||
ClientResource resourceServer = getResourceServer();
|
||||
authzClient = createAuthzClient(resourceServer.toRepresentation());
|
||||
AuthorizationResource authorization = resourceServer.authorization();
|
||||
|
||||
for (int i = 0; i < 30; i++) {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setOwnerManagedAccess(true);
|
||||
|
||||
try {
|
||||
resource.setOwner(
|
||||
JsonSerialization.readValue(new JWSInput(tokenUtil.getToken()).getContent(), AccessToken.class)
|
||||
.getSubject());
|
||||
} catch (Exception cause) {
|
||||
throw new RuntimeException("Failed to parse access token", cause);
|
||||
}
|
||||
|
||||
resource.setName("Resource " + i);
|
||||
resource.setDisplayName("Display Name " + i);
|
||||
resource.setIconUri("Icon Uri " + i);
|
||||
resource.addScope("Scope A", "Scope B", "Scope C", "Scope D");
|
||||
resource.setUri("http://resourceServer.com/resources/" + i);
|
||||
|
||||
try (Response response1 = authorization.resources().create(resource)) {
|
||||
resource.setId(response1.readEntity(ResourceRepresentation.class).getId());
|
||||
}
|
||||
|
||||
for (String scope : Arrays.asList("Scope A", "Scope B")) {
|
||||
PermissionTicketRepresentation ticket = new PermissionTicketRepresentation();
|
||||
|
||||
ticket.setGranted(true);
|
||||
ticket.setOwner(resource.getOwner().getId());
|
||||
ticket.setRequesterName(userNames.get(i % userNames.size()));
|
||||
ticket.setResource(resource.getId());
|
||||
ticket.setScopeName(scope);
|
||||
|
||||
authzClient.protection("test-user@localhost", "password").permission().create(ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ClientResource getResourceServer() {
|
||||
ClientsResource clients = testRealm().clients();
|
||||
return clients.get(clients.findByClientId("my-resource-server").get(0).getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void after() {
|
||||
super.after();
|
||||
ClientResource resourceServer = getResourceServer();
|
||||
ClientRepresentation representation = resourceServer.toRepresentation();
|
||||
representation.setAuthorizationServicesEnabled(false);
|
||||
resourceServer.update(representation);
|
||||
representation.setAuthorizationServicesEnabled(true);
|
||||
resourceServer.update(representation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMyResources() {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSharedWithMe() {
|
||||
for (String userName : userNames) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSharedWithOthers() {
|
||||
List<AbstractResourceService.ResourcePermission> resources = doGet("/shared-with-others",
|
||||
new TypeReference<List<AbstractResourceService.ResourcePermission>>() {
|
||||
});
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResource() {
|
||||
Resource resource = doGet("/" + getMyResources().get(0).getId(), Resource.class);
|
||||
|
||||
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(4, resource.getScopes().size());
|
||||
|
||||
OAuth2ErrorRepresentation response = doGet("/invalid_resource", OAuth2ErrorRepresentation.class);
|
||||
assertEquals("resource_not_found", response.getError());
|
||||
|
||||
response = doGet("/" + getMyResources().get(0).getId(), authzClient.obtainAccessToken("jdoe", "password").getToken(), OAuth2ErrorRepresentation.class);
|
||||
assertEquals("invalid_resource", response.getError());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPermissions() throws Exception {
|
||||
Resource resource = getMyResources().get(0);
|
||||
List<Permission> shares = doGet("/" + resource.getId() + "/permissions", new TypeReference<List<Permission>>() {});
|
||||
|
||||
assertEquals(1, shares.size());
|
||||
|
||||
Permission firstShare = shares.get(0);
|
||||
List<Permission> permissions = new ArrayList<>();
|
||||
|
||||
assertTrue(userNames.contains(firstShare.getUsername()));
|
||||
assertEquals(2, firstShare.getScopes().size());
|
||||
|
||||
List<String> users = new ArrayList<>(userNames);
|
||||
|
||||
users.remove(firstShare.getUsername());
|
||||
|
||||
for (String userName : users) {
|
||||
Permission permission = new Permission();
|
||||
|
||||
permission.setUsername(userName);
|
||||
permission.addScope("Scope D");
|
||||
|
||||
permissions.add(permission);
|
||||
}
|
||||
|
||||
SimpleHttp.doPut(getAccountUrl("resources/" + resource.getId() + "/permissions"), httpClient)
|
||||
.auth(tokenUtil.getToken())
|
||||
.json(permissions).asResponse();
|
||||
|
||||
shares = doGet("/" + resource.getId() + "/permissions", new TypeReference<List<Permission>>() {});
|
||||
|
||||
assertEquals(3, shares.size());
|
||||
|
||||
for (Permission user : shares) {
|
||||
assertTrue(userNames.contains(user.getUsername()));
|
||||
|
||||
if (firstShare.getUsername().equals(user.getUsername())) {
|
||||
assertEquals(2, user.getScopes().size());
|
||||
} else {
|
||||
assertEquals(1, user.getScopes().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShareResource() throws Exception {
|
||||
List<String> users = Arrays.asList("jdoe", "alice");
|
||||
List<Permission> permissions = new ArrayList<>();
|
||||
AbstractResourceService.ResourcePermission sharedResource = null;
|
||||
|
||||
for (String user : users) {
|
||||
sharedResource = getSharedWithMe(user).get(0);
|
||||
|
||||
assertNotNull(sharedResource);
|
||||
assertEquals(2, sharedResource.getScopes().size());
|
||||
}
|
||||
|
||||
permissions.add(new Permission(users.get(0), "Scope C", "Scope D"));
|
||||
permissions.add(new Permission(users.get(users.size() - 1), "Scope A", "Scope B", "Scope C", "Scope D"));
|
||||
|
||||
String resourceId = sharedResource.getId();
|
||||
SimpleHttp.Response response = SimpleHttp.doPut(getAccountUrl("resources/" + resourceId + "/permissions"), httpClient)
|
||||
.auth(tokenUtil.getToken())
|
||||
.json(permissions).asResponse();
|
||||
|
||||
assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
||||
|
||||
for (String user : users) {
|
||||
sharedResource = getSharedWithMe(user).stream()
|
||||
.filter(resource1 -> resource1.getId().equals(resourceId)).findAny().orElse(null);
|
||||
|
||||
assertNotNull(sharedResource);
|
||||
|
||||
if (user.equals(users.get(users.size() - 1))) {
|
||||
assertEquals(4, sharedResource.getScopes().size());
|
||||
} else {
|
||||
assertEquals(2, sharedResource.getScopes().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failShareResourceInvalidPermissions() throws Exception {
|
||||
List<Permission> permissions = new ArrayList<>();
|
||||
|
||||
SimpleHttp.Response response = SimpleHttp.doPut(getAccountUrl("resources/" + getMyResources().get(0).getId() + "/permissions"), httpClient)
|
||||
.auth(tokenUtil.getToken())
|
||||
.json(permissions).asResponse();
|
||||
|
||||
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevokePermission() throws Exception {
|
||||
List<String> users = Arrays.asList("jdoe", "alice");
|
||||
List<Permission> permissions = new ArrayList<>();
|
||||
AbstractResourceService.ResourcePermission sharedResource = null;
|
||||
|
||||
for (String user : users) {
|
||||
sharedResource = getSharedWithMe(user).get(0);
|
||||
|
||||
assertNotNull(sharedResource);
|
||||
assertEquals(2, sharedResource.getScopes().size());
|
||||
}
|
||||
|
||||
permissions.add(new Permission(users.get(0), "Scope C"));
|
||||
permissions.add(new Permission(users.get(users.size() - 1), "Scope B", "Scope D"));
|
||||
|
||||
String resourceId = sharedResource.getId();
|
||||
SimpleHttp.Response response = SimpleHttp.doPut(getAccountUrl("resources/" + resourceId + "/permissions"), httpClient)
|
||||
.auth(tokenUtil.getToken())
|
||||
.json(permissions).asResponse();
|
||||
|
||||
assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
||||
|
||||
for (String user : users) {
|
||||
sharedResource = getSharedWithMe(user).stream()
|
||||
.filter(resource1 -> resource1.getId().equals(resourceId)).findAny().orElse(null);
|
||||
|
||||
assertNotNull(sharedResource);
|
||||
|
||||
if (user.equals(users.get(users.size() - 1))) {
|
||||
assertEquals(2, sharedResource.getScopes().size());
|
||||
} else {
|
||||
assertEquals(1, sharedResource.getScopes().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPermissionRequests() {
|
||||
Resource resource = getMyResources().get(0);
|
||||
List<Permission> requests = doGet("/" + resource.getId() + "/permissions/requests",
|
||||
new TypeReference<List<Permission>>() {});
|
||||
|
||||
assertTrue(requests.isEmpty());
|
||||
|
||||
for (String userName : userNames) {
|
||||
List<String> scopes = new ArrayList<>();
|
||||
|
||||
if ("bob".equals(userName)) {
|
||||
scopes.add("Scope D");
|
||||
} else if ("alice".equals(userName)) {
|
||||
scopes.add("Scope C");
|
||||
} else if ("jdoe".equals(userName)) {
|
||||
scopes.add("Scope C");
|
||||
scopes.add("Scope D");
|
||||
}
|
||||
|
||||
for (String scope : scopes) {
|
||||
PermissionTicketRepresentation ticket = new PermissionTicketRepresentation();
|
||||
|
||||
ticket.setGranted(false);
|
||||
ticket.setOwner("test-user@localhost");
|
||||
ticket.setRequesterName(userName);
|
||||
ticket.setResource(resource.getId());
|
||||
ticket.setScopeName(scope);
|
||||
|
||||
authzClient.protection("test-user@localhost", "password").permission().create(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
requests = doGet("/" + resource.getId() + "/permissions/requests",
|
||||
new TypeReference<List<Permission>>() {});
|
||||
|
||||
assertEquals(3, requests.size());
|
||||
|
||||
Iterator<Permission> iterator = requests.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Permission permission = iterator.next();
|
||||
String username = permission.getUsername();
|
||||
List<String> scopes = permission.getScopes();
|
||||
|
||||
if ("bob".equals(username)) {
|
||||
assertEquals(1, scopes.size());
|
||||
assertTrue(scopes.contains("Scope D"));
|
||||
iterator.remove();
|
||||
} else if ("alice".equals(username)) {
|
||||
assertEquals(1, scopes.size());
|
||||
assertTrue(scopes.contains("Scope C"));
|
||||
iterator.remove();
|
||||
} else if ("jdoe".equals(username)) {
|
||||
assertEquals(2, scopes.size());
|
||||
assertTrue(scopes.contains("Scope C"));
|
||||
assertTrue(scopes.contains("Scope D"));
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(requests.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApprovePermissionRequest() throws IOException {
|
||||
Resource resource = getMyResources().get(0);
|
||||
List<Permission> requests = doGet("/" + resource.getId() + "/permissions/requests",
|
||||
new TypeReference<List<Permission>>() {});
|
||||
|
||||
assertTrue(requests.isEmpty());
|
||||
|
||||
for (String userName : userNames) {
|
||||
List<String> scopes = new ArrayList<>();
|
||||
|
||||
if ("bob".equals(userName)) {
|
||||
scopes.add("Scope D");
|
||||
} else if ("alice".equals(userName)) {
|
||||
scopes.add("Scope C");
|
||||
} else if ("jdoe".equals(userName)) {
|
||||
scopes.add("Scope C");
|
||||
scopes.add("Scope D");
|
||||
}
|
||||
|
||||
for (String scope : scopes) {
|
||||
PermissionTicketRepresentation ticket = new PermissionTicketRepresentation();
|
||||
|
||||
ticket.setGranted(false);
|
||||
ticket.setOwner("test-user@localhost");
|
||||
ticket.setRequesterName(userName);
|
||||
ticket.setResource(resource.getId());
|
||||
ticket.setScopeName(scope);
|
||||
|
||||
authzClient.protection("test-user@localhost", "password").permission().create(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
requests = doGet("/" + resource.getId() + "/permissions/requests",
|
||||
new TypeReference<List<Permission>>() {});
|
||||
|
||||
assertEquals(3, requests.size());
|
||||
|
||||
Iterator<Permission> iterator = requests.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Permission permission = iterator.next();
|
||||
String username = permission.getUsername();
|
||||
List<String> scopes = permission.getScopes();
|
||||
|
||||
if ("bob".equals(username)) {
|
||||
scopes.clear();
|
||||
} else if ("jdoe".equals(username)) {
|
||||
scopes.remove("Scope C");
|
||||
}
|
||||
}
|
||||
|
||||
SimpleHttp.doPut(getAccountUrl("resources/" + resource.getId() + "/permissions"), httpClient)
|
||||
.auth(tokenUtil.getToken())
|
||||
.json(requests).asResponse();
|
||||
|
||||
requests = doGet("/" + resource.getId() + "/permissions/requests",
|
||||
new TypeReference<List<Permission>>() {});
|
||||
|
||||
assertTrue(requests.isEmpty());
|
||||
|
||||
for (String user : Arrays.asList("alice", "jdoe")) {
|
||||
AbstractResourceService.ResourcePermission sharedResource = getSharedWithMe(user).stream()
|
||||
.filter(resource1 -> resource1.getId().equals(resource.getId())).findAny().orElse(null);
|
||||
|
||||
assertNotNull(sharedResource);
|
||||
|
||||
Set<ScopeRepresentation> scopes = sharedResource.getScopes();
|
||||
|
||||
if ("alice".equals(user)) {
|
||||
assertEquals(1, scopes.size());
|
||||
assertTrue(scopes.stream().anyMatch(scope -> "Scope C".equals(scope.getName())));
|
||||
} else if ("jdoe".equals(user)) {
|
||||
assertEquals(1, scopes.size());
|
||||
assertTrue(scopes.stream().anyMatch(scope -> "Scope D".equals(scope.getName())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<AbstractResourceService.ResourcePermission> getSharedWithMe(String userName) {
|
||||
return doGet("/shared-with-me", authzClient.obtainAccessToken(userName, "password").getToken(),
|
||||
new TypeReference<List<AbstractResourceService.ResourcePermission>>() {});
|
||||
}
|
||||
|
||||
private <R> R doGet(String resource, TypeReference<R> typeReference) {
|
||||
return doGet(resource, tokenUtil.getToken(), typeReference);
|
||||
}
|
||||
|
||||
private <R> R doGet(String resource, Class<R> type) {
|
||||
return doGet(resource, tokenUtil.getToken(), type);
|
||||
}
|
||||
|
||||
private <R> R doGet(String resource, String token, TypeReference<R> typeReference) {
|
||||
try {
|
||||
return get(resource, token).asJson(typeReference);
|
||||
} 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);
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to fetch resource", cause);
|
||||
}
|
||||
}
|
||||
|
||||
private SimpleHttp get(String resource, String token) {
|
||||
return SimpleHttp.doGet(getAccountUrl("resources" + resource), httpClient).auth(token);
|
||||
}
|
||||
|
||||
private AuthzClient createAuthzClient(ClientRepresentation client) {
|
||||
Map<String, Object> credentials = new HashMap<>();
|
||||
|
||||
credentials.put("secret", "secret");
|
||||
|
||||
return AuthzClient
|
||||
.create(new Configuration(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth",
|
||||
testRealm().toRepresentation().getRealm(), client.getClientId(),
|
||||
credentials, httpClient));
|
||||
}
|
||||
|
||||
private UserRepresentation createUser(String userName, String password) {
|
||||
return UserBuilder.create()
|
||||
.username(userName)
|
||||
.enabled(true)
|
||||
.password(password)
|
||||
.role("account", AccountRoles.MANAGE_ACCOUNT)
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<Resource> getMyResources() {
|
||||
return doGet("", new TypeReference<List<Resource>>() {});
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue