Merge pull request #5203 from martel-innovate/separate-ticket-permission-and-uma-permission-API
[KEYCLOAK-7354] - Split ticket management and permission endpoint
This commit is contained in:
commit
e5d997a6c0
5 changed files with 266 additions and 143 deletions
|
@ -94,6 +94,41 @@ public class PermissionResource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new uma permission for a single resource and scope(s).
|
||||||
|
*
|
||||||
|
* @param ticket the {@link PermissionTicketRepresentation} representing the resource and scope(s) (not {@code null})
|
||||||
|
* @return a permission response holding the permission ticket representation
|
||||||
|
*/
|
||||||
|
public PermissionTicketRepresentation create(final PermissionTicketRepresentation ticket) {
|
||||||
|
if (ticket == null) {
|
||||||
|
throw new IllegalArgumentException("Permission ticket must not be null or empty");
|
||||||
|
}
|
||||||
|
if (ticket.getRequester() == null || ticket.getRequesterName() == null) {
|
||||||
|
throw new IllegalArgumentException("Permission ticket must have a requester");
|
||||||
|
}
|
||||||
|
if (ticket.getResource() == null || ticket.getResourceName() == null) {
|
||||||
|
throw new IllegalArgumentException("Permission ticket must have a resource");
|
||||||
|
}
|
||||||
|
if (ticket.getScope() == null || ticket.getScopeName() == null) {
|
||||||
|
throw new IllegalArgumentException("Permission ticket must have a scope");
|
||||||
|
}
|
||||||
|
Callable<PermissionTicketRepresentation> callable = new Callable<PermissionTicketRepresentation>() {
|
||||||
|
@Override
|
||||||
|
public PermissionTicketRepresentation call() throws Exception {
|
||||||
|
return http.<PermissionTicketRepresentation>post(serverConfiguration.getPermissionEndpoint()+"/ticket")
|
||||||
|
.json(JsonSerialization.writeValueAsBytes(ticket))
|
||||||
|
.authorizationBearer(pat.call())
|
||||||
|
.response().json(new TypeReference<PermissionTicketRepresentation>(){}).execute();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
return callable.call();
|
||||||
|
} catch (Exception cause) {
|
||||||
|
return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error updating permission ticket", cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the server for any permission ticket associated with the given <code>scopeId</code>.
|
* Query the server for any permission ticket associated with the given <code>scopeId</code>.
|
||||||
*
|
*
|
||||||
|
@ -107,7 +142,7 @@ public class PermissionResource {
|
||||||
Callable<List<PermissionTicketRepresentation>> callable = new Callable<List<PermissionTicketRepresentation>>() {
|
Callable<List<PermissionTicketRepresentation>> callable = new Callable<List<PermissionTicketRepresentation>>() {
|
||||||
@Override
|
@Override
|
||||||
public List<PermissionTicketRepresentation> call() throws Exception {
|
public List<PermissionTicketRepresentation> call() throws Exception {
|
||||||
return http.<List<PermissionTicketRepresentation>>get(serverConfiguration.getPermissionEndpoint())
|
return http.<List<PermissionTicketRepresentation>>get(serverConfiguration.getPermissionEndpoint()+"/ticket")
|
||||||
.authorizationBearer(pat.call())
|
.authorizationBearer(pat.call())
|
||||||
.param("scopeId", scopeId)
|
.param("scopeId", scopeId)
|
||||||
.response().json(new TypeReference<List<PermissionTicketRepresentation>>(){}).execute();
|
.response().json(new TypeReference<List<PermissionTicketRepresentation>>(){}).execute();
|
||||||
|
@ -133,7 +168,7 @@ public class PermissionResource {
|
||||||
Callable<List<PermissionTicketRepresentation>> callable = new Callable<List<PermissionTicketRepresentation>>() {
|
Callable<List<PermissionTicketRepresentation>> callable = new Callable<List<PermissionTicketRepresentation>>() {
|
||||||
@Override
|
@Override
|
||||||
public List<PermissionTicketRepresentation> call() throws Exception {
|
public List<PermissionTicketRepresentation> call() throws Exception {
|
||||||
return http.<List<PermissionTicketRepresentation>>get(serverConfiguration.getPermissionEndpoint())
|
return http.<List<PermissionTicketRepresentation>>get(serverConfiguration.getPermissionEndpoint()+"/ticket")
|
||||||
.authorizationBearer(pat.call())
|
.authorizationBearer(pat.call())
|
||||||
.param("resourceId", resourceId)
|
.param("resourceId", resourceId)
|
||||||
.response().json(new TypeReference<List<PermissionTicketRepresentation>>(){}).execute();
|
.response().json(new TypeReference<List<PermissionTicketRepresentation>>(){}).execute();
|
||||||
|
@ -170,7 +205,7 @@ public class PermissionResource {
|
||||||
Callable<List<PermissionTicketRepresentation>> callable = new Callable<List<PermissionTicketRepresentation>>() {
|
Callable<List<PermissionTicketRepresentation>> callable = new Callable<List<PermissionTicketRepresentation>>() {
|
||||||
@Override
|
@Override
|
||||||
public List<PermissionTicketRepresentation> call() throws Exception {
|
public List<PermissionTicketRepresentation> call() throws Exception {
|
||||||
return http.<List<PermissionTicketRepresentation>>get(serverConfiguration.getPermissionEndpoint())
|
return http.<List<PermissionTicketRepresentation>>get(serverConfiguration.getPermissionEndpoint()+"/ticket")
|
||||||
.authorizationBearer(pat.call())
|
.authorizationBearer(pat.call())
|
||||||
.param("resourceId", resourceId)
|
.param("resourceId", resourceId)
|
||||||
.param("scopeId", scopeId)
|
.param("scopeId", scopeId)
|
||||||
|
@ -205,7 +240,7 @@ public class PermissionResource {
|
||||||
Callable callable = new Callable() {
|
Callable callable = new Callable() {
|
||||||
@Override
|
@Override
|
||||||
public Object call() throws Exception {
|
public Object call() throws Exception {
|
||||||
http.<List>put(serverConfiguration.getPermissionEndpoint())
|
http.<List>put(serverConfiguration.getPermissionEndpoint()+"/ticket")
|
||||||
.json(JsonSerialization.writeValueAsBytes(ticket))
|
.json(JsonSerialization.writeValueAsBytes(ticket))
|
||||||
.authorizationBearer(pat.call())
|
.authorizationBearer(pat.call())
|
||||||
.response().json(List.class).execute();
|
.response().json(List.class).execute();
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
import org.keycloak.authorization.protection.permission.PermissionTicketService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
@ -82,6 +83,17 @@ public class ProtectionService {
|
||||||
|
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("/permission/ticket")
|
||||||
|
public Object ticket() {
|
||||||
|
KeycloakIdentity identity = createIdentity(false);
|
||||||
|
|
||||||
|
PermissionTicketService resource = new PermissionTicketService(identity, getResourceServer(identity), this.authorization);
|
||||||
|
|
||||||
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
private KeycloakIdentity createIdentity(boolean checkProtectionScope) {
|
private KeycloakIdentity createIdentity(boolean checkProtectionScope) {
|
||||||
KeycloakIdentity identity = new KeycloakIdentity(this.authorization.getKeycloakSession());
|
KeycloakIdentity identity = new KeycloakIdentity(this.authorization.getKeycloakSession());
|
||||||
|
|
|
@ -17,31 +17,16 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.authorization.protection.permission;
|
package org.keycloak.authorization.protection.permission;
|
||||||
|
|
||||||
import org.keycloak.OAuthErrorException;
|
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.common.KeycloakIdentity;
|
import org.keycloak.authorization.common.KeycloakIdentity;
|
||||||
import org.keycloak.authorization.model.PermissionTicket;
|
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.representations.idm.authorization.PermissionRequest;
|
import org.keycloak.representations.idm.authorization.PermissionRequest;
|
||||||
import org.keycloak.authorization.store.PermissionTicketStore;
|
|
||||||
import org.keycloak.models.Constants;
|
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
|
||||||
import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
|
|
||||||
import org.keycloak.services.ErrorResponseException;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.PUT;
|
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
@ -64,82 +49,4 @@ public class PermissionService extends AbstractPermissionService {
|
||||||
return super.create(request);
|
return super.create(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PUT
|
|
||||||
@Consumes("application/json")
|
|
||||||
public Response update(PermissionTicketRepresentation representation) {
|
|
||||||
if (representation == null || representation.getId() == null) {
|
|
||||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
|
|
||||||
PermissionTicket ticket = ticketStore.findById(representation.getId(), resourceServer.getId());
|
|
||||||
|
|
||||||
if (ticket == null) {
|
|
||||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
RepresentationToModel.toModel(representation, resourceServer.getId(), authorization);
|
|
||||||
|
|
||||||
return Response.noContent().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@DELETE
|
|
||||||
@Consumes("application/json")
|
|
||||||
public Response delete(String id) {
|
|
||||||
if (id == null) {
|
|
||||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
|
|
||||||
PermissionTicket ticket = ticketStore.findById(id, resourceServer.getId());
|
|
||||||
|
|
||||||
if (ticket == null) {
|
|
||||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
ticketStore.delete(id);
|
|
||||||
|
|
||||||
return Response.noContent().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Produces("application/json")
|
|
||||||
public Response find(@QueryParam("scopeId") String scopeId,
|
|
||||||
@QueryParam("resourceId") String resourceId,
|
|
||||||
@QueryParam("owner") String owner,
|
|
||||||
@QueryParam("requester") String requester,
|
|
||||||
@QueryParam("granted") Boolean granted,
|
|
||||||
@QueryParam("returnNames") Boolean returnNames,
|
|
||||||
@QueryParam("first") Integer firstResult,
|
|
||||||
@QueryParam("max") Integer maxResult) {
|
|
||||||
PermissionTicketStore permissionTicketStore = authorization.getStoreFactory().getPermissionTicketStore();
|
|
||||||
|
|
||||||
Map<String, String> filters = new HashMap<>();
|
|
||||||
|
|
||||||
if (resourceId != null) {
|
|
||||||
filters.put(PermissionTicket.RESOURCE, resourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scopeId != null) {
|
|
||||||
filters.put(PermissionTicket.SCOPE, scopeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (owner != null) {
|
|
||||||
filters.put(PermissionTicket.OWNER, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requester != null) {
|
|
||||||
filters.put(PermissionTicket.REQUESTER, requester);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (granted != null) {
|
|
||||||
filters.put(PermissionTicket.GRANTED, granted.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Response.ok().entity(permissionTicketStore.find(filters, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS)
|
|
||||||
.stream()
|
|
||||||
.map(permissionTicket -> ModelToRepresentation.toRepresentation(permissionTicket, authorization, returnNames == null ? false : returnNames))
|
|
||||||
.collect(Collectors.toList()))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2016 Red Hat, Inc., and individual 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.authorization.protection.permission;
|
||||||
|
|
||||||
|
import org.keycloak.OAuthErrorException;
|
||||||
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
|
import org.keycloak.authorization.common.KeycloakIdentity;
|
||||||
|
import org.keycloak.authorization.model.PermissionTicket;
|
||||||
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
|
import org.keycloak.authorization.store.PermissionTicketStore;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
|
||||||
|
import org.keycloak.services.ErrorResponseException;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import org.keycloak.authorization.model.Resource;
|
||||||
|
import org.keycloak.authorization.model.Scope;
|
||||||
|
import org.keycloak.authorization.store.ResourceStore;
|
||||||
|
import org.keycloak.authorization.store.ScopeStore;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
public class PermissionTicketService {
|
||||||
|
|
||||||
|
private final AuthorizationProvider authorization;
|
||||||
|
private final KeycloakIdentity identity;
|
||||||
|
private final ResourceServer resourceServer;
|
||||||
|
|
||||||
|
public PermissionTicketService(KeycloakIdentity identity, ResourceServer resourceServer, AuthorizationProvider authorization) {
|
||||||
|
this.identity = identity;
|
||||||
|
this.resourceServer = resourceServer;
|
||||||
|
this.authorization = authorization;
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes("application/json")
|
||||||
|
@Produces("application/json")
|
||||||
|
public Response create(PermissionTicketRepresentation representation) {
|
||||||
|
PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
|
||||||
|
if (representation == null)
|
||||||
|
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_permission", Response.Status.BAD_REQUEST);
|
||||||
|
if (representation.getId() != null)
|
||||||
|
throw new ErrorResponseException("invalid_permission", "created permissions should not have id", Response.Status.BAD_REQUEST);
|
||||||
|
if (representation.getResource() == null)
|
||||||
|
throw new ErrorResponseException("invalid_permission", "created permissions should have resource", Response.Status.BAD_REQUEST);
|
||||||
|
if (representation.getScope() == null && representation.getScopeName() == null)
|
||||||
|
throw new ErrorResponseException("invalid_permission", "created permissions should have scope or scopeName", Response.Status.BAD_REQUEST);
|
||||||
|
if (representation.getRequester() == null && representation.getRequesterName() == null)
|
||||||
|
throw new ErrorResponseException("invalid_permission", "created permissions should have requester or requesterName", Response.Status.BAD_REQUEST);
|
||||||
|
|
||||||
|
ResourceStore rstore = this.authorization.getStoreFactory().getResourceStore();
|
||||||
|
Resource resource = rstore.findById(representation.getResource(), resourceServer.getId());
|
||||||
|
if (resource == null ) throw new ErrorResponseException("invalid_resource_id", "Resource set with id [" + representation.getResource() + "] does not exists in this server.", Response.Status.BAD_REQUEST);
|
||||||
|
|
||||||
|
if (!resource.getOwner().equals(this.identity.getId()))
|
||||||
|
throw new ErrorResponseException("not_authorised", "permissions for [" + representation.getResource() + "] can be only created by the owner", Response.Status.FORBIDDEN);
|
||||||
|
|
||||||
|
UserModel user = null;
|
||||||
|
if(representation.getRequester() != null)
|
||||||
|
user = this.authorization.getKeycloakSession().userStorageManager().getUserById(representation.getRequester(), this.authorization.getRealm());
|
||||||
|
else
|
||||||
|
user = this.authorization.getKeycloakSession().userStorageManager().getUserByUsername(representation.getRequesterName(), this.authorization.getRealm());
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
throw new ErrorResponseException("invalid_permission", "Requester does not exists in this server as user.", Response.Status.BAD_REQUEST);
|
||||||
|
|
||||||
|
Scope scope = null;
|
||||||
|
ScopeStore sstore = this.authorization.getStoreFactory().getScopeStore();
|
||||||
|
|
||||||
|
if(representation.getScopeName() != null)
|
||||||
|
scope = sstore.findByName(representation.getScopeName(), resourceServer.getId());
|
||||||
|
else
|
||||||
|
scope = sstore.findById(representation.getScope(), resourceServer.getId());
|
||||||
|
|
||||||
|
if (scope == null && representation.getScope() !=null )
|
||||||
|
throw new ErrorResponseException("invalid_scope", "Scope [" + representation.getScope() + "] is invalid", Response.Status.BAD_REQUEST);
|
||||||
|
if (scope == null && representation.getScopeName() !=null )
|
||||||
|
throw new ErrorResponseException("invalid_scope", "Scope [" + representation.getScopeName() + "] is invalid", Response.Status.BAD_REQUEST);
|
||||||
|
|
||||||
|
boolean match = resource.getScopes().contains(scope);
|
||||||
|
|
||||||
|
if (!match)
|
||||||
|
throw new ErrorResponseException("invalid_resource_id", "Resource set with id [" + representation.getResource() + "] does not have Scope [" + scope.getName() + "]", Response.Status.BAD_REQUEST);
|
||||||
|
|
||||||
|
Map<String, String> attributes = new HashMap<String, String>();
|
||||||
|
attributes.put(PermissionTicket.RESOURCE, resource.getId());
|
||||||
|
attributes.put(PermissionTicket.SCOPE, scope.getId());
|
||||||
|
attributes.put(PermissionTicket.REQUESTER, user.getId());
|
||||||
|
|
||||||
|
if (!ticketStore.find(attributes, resourceServer.getId(), -1, -1).isEmpty())
|
||||||
|
throw new ErrorResponseException("invalid_permission", "Permission already exists", Response.Status.BAD_REQUEST);
|
||||||
|
|
||||||
|
PermissionTicket ticket = ticketStore.create(resource.getId(), scope.getId(), user.getId(), resourceServer);
|
||||||
|
representation = ModelToRepresentation.toRepresentation(ticket, authorization);
|
||||||
|
return Response.ok(representation).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Consumes("application/json")
|
||||||
|
public Response update(PermissionTicketRepresentation representation) {
|
||||||
|
if (representation == null || representation.getId() == null) {
|
||||||
|
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
|
||||||
|
PermissionTicket ticket = ticketStore.findById(representation.getId(), resourceServer.getId());
|
||||||
|
|
||||||
|
if (ticket == null) {
|
||||||
|
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ticket.getOwner().equals(this.identity.getId()) && !this.identity.isResourceServer())
|
||||||
|
throw new ErrorResponseException("not_authorised", "permissions for [" + representation.getResource() + "] can be updated only by the owner or by the resource server", Response.Status.FORBIDDEN);
|
||||||
|
|
||||||
|
RepresentationToModel.toModel(representation, resourceServer.getId(), authorization);
|
||||||
|
|
||||||
|
return Response.noContent().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Path("{id}")
|
||||||
|
@DELETE
|
||||||
|
@Consumes("application/json")
|
||||||
|
public Response delete(@PathParam("id") String id) {
|
||||||
|
if (id == null) {
|
||||||
|
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
|
||||||
|
PermissionTicket ticket = ticketStore.findById(id, resourceServer.getId());
|
||||||
|
|
||||||
|
if (ticket == null) {
|
||||||
|
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ticket.getOwner().equals(this.identity.getId()) && !this.identity.isResourceServer() && !ticket.getRequester().equals(this.identity.getId()))
|
||||||
|
throw new ErrorResponseException("not_authorised", "permissions for [" + ticket.getResource() + "] can be deleted only by the owner, the requester, or the resource server", Response.Status.FORBIDDEN);
|
||||||
|
|
||||||
|
ticketStore.delete(id);
|
||||||
|
|
||||||
|
return Response.noContent().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces("application/json")
|
||||||
|
public Response find(@QueryParam("scopeId") String scopeId,
|
||||||
|
@QueryParam("resourceId") String resourceId,
|
||||||
|
@QueryParam("owner") String owner,
|
||||||
|
@QueryParam("requester") String requester,
|
||||||
|
@QueryParam("granted") Boolean granted,
|
||||||
|
@QueryParam("returnNames") Boolean returnNames,
|
||||||
|
@QueryParam("first") Integer firstResult,
|
||||||
|
@QueryParam("max") Integer maxResult) {
|
||||||
|
PermissionTicketStore permissionTicketStore = authorization.getStoreFactory().getPermissionTicketStore();
|
||||||
|
|
||||||
|
Map<String, String> filters = new HashMap<>();
|
||||||
|
|
||||||
|
if (resourceId != null) {
|
||||||
|
filters.put(PermissionTicket.RESOURCE, resourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scopeId != null) {
|
||||||
|
filters.put(PermissionTicket.SCOPE, scopeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (owner != null) {
|
||||||
|
filters.put(PermissionTicket.OWNER, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requester != null) {
|
||||||
|
filters.put(PermissionTicket.REQUESTER, requester);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (granted != null) {
|
||||||
|
filters.put(PermissionTicket.GRANTED, granted.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.ok().entity(permissionTicketStore.find(filters, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS)
|
||||||
|
.stream()
|
||||||
|
.map(permissionTicket -> ModelToRepresentation.toRepresentation(permissionTicket, authorization, returnNames == null ? false : returnNames))
|
||||||
|
.collect(Collectors.toList()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* JBoss, Home of Professional Open Source.
|
|
||||||
* Copyright 2016 Red Hat, Inc., and individual 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.authorization.protection.permission;
|
|
||||||
|
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
|
||||||
import org.keycloak.authorization.common.KeycloakIdentity;
|
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
|
||||||
import org.keycloak.representations.idm.authorization.PermissionRequest;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
|
||||||
import javax.ws.rs.POST;
|
|
||||||
import javax.ws.rs.Produces;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
|
||||||
*/
|
|
||||||
public class PermissionsService extends AbstractPermissionService {
|
|
||||||
|
|
||||||
public PermissionsService(KeycloakIdentity identity, ResourceServer resourceServer, AuthorizationProvider authorization) {
|
|
||||||
super(identity, resourceServer, authorization);
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Consumes("application/json")
|
|
||||||
@Produces("application/json")
|
|
||||||
public Response create(List<PermissionRequest> request) {
|
|
||||||
return super.create(request);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue