[KEYCLOAK-4904] - Authorization Audit - Part 1

This commit is contained in:
Pedro Igor 2017-06-09 13:31:06 -03:00
parent 9be9e30ad6
commit f12cef2c86
18 changed files with 254 additions and 95 deletions

View file

@ -156,5 +156,25 @@ public enum ResourceType {
/**
*
*/
, COMPONENT;
, COMPONENT
/**
*
*/
, AUTHORIZATION_RESOURCE_SERVER
/**
*
*/
, AUTHORIZATION_RESOURCE
/**
*
*/
, AUTHORIZATION_SCOPE
/**
*
*/
, AUTHORIZATION_POLICY;
}

View file

@ -772,11 +772,7 @@ public class ModelToRepresentation {
return rep;
}
public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider) {
return toRepresentation(model, authorizationProvider, true);
}
public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider, boolean deep) {
public static ScopeRepresentation toRepresentation(Scope model) {
ScopeRepresentation scope = new ScopeRepresentation();
scope.setId(model.getId());

View file

@ -23,6 +23,7 @@ import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.services.resources.admin.RealmAuth;
import javax.ws.rs.Path;
@ -34,14 +35,14 @@ public class AuthorizationService {
private final RealmAuth auth;
private final ClientModel client;
private final KeycloakSession session;
private final ResourceServer resourceServer;
private final AuthorizationProvider authorization;
private final AdminEventBuilder adminEvent;
public AuthorizationService(KeycloakSession session, ClientModel client, RealmAuth auth) {
this.session = session;
public AuthorizationService(KeycloakSession session, ClientModel client, RealmAuth auth, AdminEventBuilder adminEvent) {
this.client = client;
this.authorization = session.getProvider(AuthorizationProvider.class);
this.adminEvent = adminEvent;
this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().findByClient(this.client.getId());
this.auth = auth;
@ -52,16 +53,16 @@ public class AuthorizationService {
@Path("/resource-server")
public ResourceServerService resourceServer() {
ResourceServerService resource = new ResourceServerService(this.authorization, this.resourceServer, this.client, this.auth);
ResourceServerService resource = new ResourceServerService(this.authorization, this.resourceServer, this.client, this.auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);
return resource;
}
public void enable() {
public void enable(boolean newClient) {
if (!isEnabled()) {
resourceServer().create();
resourceServer().create(newClient);
}
}

View file

@ -22,6 +22,7 @@ import java.util.Map;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.services.resources.admin.RealmAuth;
/**
@ -29,18 +30,18 @@ import org.keycloak.services.resources.admin.RealmAuth;
*/
public class PermissionService extends PolicyService {
public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
super(resourceServer, authorization, auth);
public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
super(resourceServer, authorization, auth, adminEvent);
}
@Override
protected PolicyResourceService doCreatePolicyResource(Policy policy) {
return new PolicyTypeResourceService(policy, resourceServer, authorization, auth);
return new PolicyTypeResourceService(policy, resourceServer, authorization, auth, adminEvent);
}
@Override
protected PolicyTypeService doCreatePolicyTypeResource(String type) {
return new PolicyTypeService(type, resourceServer, authorization, auth) {
return new PolicyTypeService(type, resourceServer, authorization, auth, adminEvent) {
@Override
protected List<Object> doSearch(Integer firstResult, Integer maxResult, Map<String, String[]> filters) {
filters.put("permission", new String[] {Boolean.TRUE.toString()});

View file

@ -26,8 +26,10 @@ 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.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.authorization.AuthorizationProvider;
@ -36,12 +38,15 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
@ -54,19 +59,21 @@ public class PolicyResourceService {
protected final ResourceServer resourceServer;
protected final AuthorizationProvider authorization;
protected final RealmAuth auth;
private final AdminEventBuilder adminEvent;
public PolicyResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
public PolicyResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
this.policy = policy;
this.resourceServer = resourceServer;
this.authorization = authorization;
this.auth = auth;
this.adminEvent = adminEvent.resource(ResourceType.AUTHORIZATION_POLICY);
}
@PUT
@Consumes("application/json")
@Produces("application/json")
@NoCache
public Response update(String payload) {
public Response update(@Context UriInfo uriInfo, String payload) {
this.auth.requireManage();
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
@ -79,11 +86,14 @@ public class PolicyResourceService {
RepresentationToModel.toModel(representation, authorization, policy);
audit(uriInfo, representation, OperationType.UPDATE);
return Response.status(Status.CREATED).build();
}
@DELETE
public Response delete() {
public Response delete(@Context UriInfo uriInfo) {
this.auth.requireManage();
if (policy == null) {
@ -98,6 +108,10 @@ public class PolicyResourceService {
policyStore.delete(policy.getId());
if (authorization.getRealm().isAdminEventsEnabled()) {
audit(uriInfo, toRepresentation(policy, authorization), OperationType.DELETE);
}
return Response.noContent().build();
}
@ -225,4 +239,10 @@ public class PolicyResourceService {
protected Policy getPolicy() {
return policy;
}
private void audit(@Context UriInfo uriInfo, AbstractPolicyRepresentation policy, OperationType operation) {
if (authorization.getRealm().isAdminEventsEnabled()) {
adminEvent.operation(operation).resourcePath(uriInfo).representation(policy).success();
}
}
}

View file

@ -33,9 +33,11 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
@ -46,12 +48,16 @@ import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.Constants;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
@ -63,11 +69,13 @@ public class PolicyService {
protected final ResourceServer resourceServer;
protected final AuthorizationProvider authorization;
protected final RealmAuth auth;
protected final AdminEventBuilder adminEvent;
public PolicyService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
public PolicyService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
this.resourceServer = resourceServer;
this.authorization = authorization;
this.auth = auth;
this.adminEvent = adminEvent.resource(ResourceType.AUTHORIZATION_POLICY);
}
@Path("{type}")
@ -84,18 +92,18 @@ public class PolicyService {
}
protected PolicyTypeService doCreatePolicyTypeResource(String type) {
return new PolicyTypeService(type, resourceServer, authorization, auth);
return new PolicyTypeService(type, resourceServer, authorization, auth, adminEvent);
}
protected Object doCreatePolicyResource(Policy policy) {
return new PolicyResourceService(policy, resourceServer, authorization, auth);
return new PolicyResourceService(policy, resourceServer, authorization, auth, adminEvent);
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response create(String payload) {
public Response create(@Context UriInfo uriInfo, String payload) {
this.auth.requireManage();
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
@ -103,6 +111,8 @@ public class PolicyService {
representation.setId(policy.getId());
audit(uriInfo, representation, representation.getId(), OperationType.CREATE);
return Response.status(Status.CREATED).entity(representation).build();
}
@ -280,4 +290,14 @@ public class PolicyService {
findAssociatedPolicies(associated, policies);
});
}
private void audit(@Context UriInfo uriInfo, AbstractPolicyRepresentation resource, String id, OperationType operation) {
if (authorization.getRealm().isAdminEventsEnabled()) {
if (id != null) {
adminEvent.operation(operation).resourcePath(uriInfo, id).representation(resource).success();
} else {
adminEvent.operation(operation).resourcePath(uriInfo).representation(resource).success();
}
}
}
}

View file

@ -24,6 +24,7 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
@ -32,8 +33,8 @@ import org.keycloak.util.JsonSerialization;
*/
public class PolicyTypeResourceService extends PolicyResourceService {
public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
super(policy, resourceServer, authorization, auth);
public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
super(policy, resourceServer, authorization, auth, adminEvent);
}
@Override

View file

@ -30,6 +30,7 @@ import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
@ -40,8 +41,8 @@ public class PolicyTypeService extends PolicyService {
private final String type;
PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
super(resourceServer, authorization, auth);
PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
super(resourceServer, authorization, auth, adminEvent);
this.type = type;
}
@ -60,7 +61,7 @@ public class PolicyTypeService extends PolicyService {
@Override
protected Object doCreatePolicyResource(Policy policy) {
return new PolicyTypeResourceService(policy, resourceServer,authorization, auth);
return new PolicyTypeResourceService(policy, resourceServer,authorization, auth, adminEvent);
}
@Override

View file

@ -40,12 +40,15 @@ import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.exportimport.util.ExportUtils;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
@ -53,6 +56,7 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.services.resources.admin.RealmAuth;
/**
@ -62,19 +66,24 @@ public class ResourceServerService {
private final AuthorizationProvider authorization;
private final RealmAuth auth;
private final AdminEventBuilder adminEvent;
private final KeycloakSession session;
private ResourceServer resourceServer;
private final ClientModel client;
public ResourceServerService(AuthorizationProvider authorization, ResourceServer resourceServer, ClientModel client, RealmAuth auth) {
@Context
private UriInfo uriInfo;
public ResourceServerService(AuthorizationProvider authorization, ResourceServer resourceServer, ClientModel client, RealmAuth auth, AdminEventBuilder adminEvent) {
this.authorization = authorization;
this.session = authorization.getKeycloakSession();
this.client = client;
this.resourceServer = resourceServer;
this.auth = auth;
this.adminEvent = adminEvent;
}
public void create() {
public void create(boolean newClient) {
this.auth.requireManage();
UserModel serviceAccount = this.session.users().getServiceAccount(client);
@ -86,16 +95,17 @@ public class ResourceServerService {
this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().create(this.client.getId());
createDefaultRoles(serviceAccount);
createDefaultPermission(createDefaultResource(), createDefaultPolicy());
audit(OperationType.CREATE, uriInfo, newClient);
}
@PUT
@Consumes("application/json")
@Produces("application/json")
public Response update(ResourceServerRepresentation server) {
public Response update(@Context UriInfo uriInfo, ResourceServerRepresentation server) {
this.auth.requireManage();
this.resourceServer.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement());
this.resourceServer.setPolicyEnforcementMode(server.getPolicyEnforcementMode());
audit(OperationType.UPDATE, uriInfo, false);
return Response.noContent().build();
}
@ -105,17 +115,19 @@ public class ResourceServerService {
ResourceStore resourceStore = storeFactory.getResourceStore();
String id = resourceServer.getId();
PolicyStore policyStore = storeFactory.getPolicyStore();
policyStore.findByResourceServer(id).forEach(scope -> policyStore.delete(scope.getId()));
resourceStore.findByResourceServer(id).forEach(resource -> resourceStore.delete(resource.getId()));
ScopeStore scopeStore = storeFactory.getScopeStore();
scopeStore.findByResourceServer(id).forEach(scope -> scopeStore.delete(scope.getId()));
PolicyStore policyStore = storeFactory.getPolicyStore();
policyStore.findByResourceServer(id).forEach(scope -> policyStore.delete(scope.getId()));
storeFactory.getResourceServerStore().delete(id);
audit(OperationType.DELETE, uriInfo, false);
}
@GET
@ -143,12 +155,14 @@ public class ResourceServerService {
RepresentationToModel.toModel(rep, authorization);
audit(OperationType.UPDATE, uriInfo, false);
return Response.noContent().build();
}
@Path("/resource")
public ResourceSetService getResourceSetResource() {
ResourceSetService resource = new ResourceSetService(this.resourceServer, this.authorization, this.auth);
ResourceSetService resource = new ResourceSetService(this.resourceServer, this.authorization, this.auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);
@ -157,7 +171,7 @@ public class ResourceServerService {
@Path("/scope")
public ScopeService getScopeResource() {
ScopeService resource = new ScopeService(this.resourceServer, this.authorization, this.auth);
ScopeService resource = new ScopeService(this.resourceServer, this.authorization, this.auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);
@ -166,7 +180,7 @@ public class ResourceServerService {
@Path("/policy")
public PolicyService getPolicyResource() {
PolicyService resource = new PolicyService(this.resourceServer, this.authorization, this.auth);
PolicyService resource = new PolicyService(this.resourceServer, this.authorization, this.auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);
@ -176,7 +190,7 @@ public class ResourceServerService {
@Path("/permission")
public Object getPermissionTypeResource() {
this.auth.requireView();
PermissionService resource = new PermissionService(this.resourceServer, this.authorization, this.auth);
PermissionService resource = new PermissionService(this.resourceServer, this.authorization, this.auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);
@ -239,4 +253,14 @@ public class ResourceServerService {
serviceAccount.grantRole(umaProtectionRole);
}
}
private void audit(OperationType operation, UriInfo uriInfo, boolean newClient) {
if (newClient) {
adminEvent.resource(ResourceType.AUTHORIZATION_RESOURCE_SERVER).operation(operation).resourcePath(uriInfo, client.getId())
.representation(ModelToRepresentation.toRepresentation(resourceServer, client)).success();
} else {
adminEvent.resource(ResourceType.AUTHORIZATION_RESOURCE_SERVER).operation(operation).resourcePath(uriInfo)
.representation(ModelToRepresentation.toRepresentation(resourceServer, client)).success();
}
}
}

View file

@ -26,6 +26,8 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
@ -37,6 +39,7 @@ import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentatio
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.services.resources.admin.RealmAuth;
import javax.ws.rs.Consumes;
@ -48,8 +51,10 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.Collections;
@ -70,17 +75,25 @@ public class ResourceSetService {
private final AuthorizationProvider authorization;
private final RealmAuth auth;
private final AdminEventBuilder adminEvent;
private ResourceServer resourceServer;
public ResourceSetService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
public ResourceSetService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
this.resourceServer = resourceServer;
this.authorization = authorization;
this.auth = auth;
this.adminEvent = adminEvent.resource(ResourceType.AUTHORIZATION_RESOURCE);
}
@POST
@Consumes("application/json")
@Produces("application/json")
public Response create(@Context UriInfo uriInfo, ResourceRepresentation resource) {
Response response = create(resource);
audit(uriInfo, resource, resource.getId(), OperationType.CREATE);
return response;
}
public Response create(ResourceRepresentation resource) {
requireManage();
StoreFactory storeFactory = this.authorization.getStoreFactory();
@ -128,7 +141,7 @@ public class ResourceSetService {
@PUT
@Consumes("application/json")
@Produces("application/json")
public Response update(@PathParam("id") String id, ResourceRepresentation resource) {
public Response update(@Context UriInfo uriInfo, @PathParam("id") String id, ResourceRepresentation resource) {
requireManage();
resource.setId(id);
StoreFactory storeFactory = this.authorization.getStoreFactory();
@ -141,12 +154,14 @@ public class ResourceSetService {
toModel(resource, resourceServer, authorization);
audit(uriInfo, resource, OperationType.UPDATE);
return Response.noContent().build();
}
@Path("{id}")
@DELETE
public Response delete(@PathParam("id") String id) {
public Response delete(@Context UriInfo uriInfo, @PathParam("id") String id) {
requireManage();
StoreFactory storeFactory = authorization.getStoreFactory();
Resource resource = storeFactory.getResourceStore().findById(id, resourceServer.getId());
@ -168,6 +183,10 @@ public class ResourceSetService {
storeFactory.getResourceStore().delete(id);
if (authorization.getRealm().isAdminEventsEnabled()) {
audit(uriInfo, toRepresentation(resource, resourceServer, authorization), OperationType.DELETE);
}
return Response.noContent().build();
}
@ -376,4 +395,18 @@ public class ResourceSetService {
this.auth.requireManage();
}
}
private void audit(@Context UriInfo uriInfo, ResourceRepresentation resource, OperationType operation) {
audit(uriInfo, resource, null, operation);
}
private void audit(@Context UriInfo uriInfo, ResourceRepresentation resource, String id, OperationType operation) {
if (authorization.getRealm().isAdminEventsEnabled()) {
if (id != null) {
adminEvent.operation(operation).resourcePath(uriInfo, id).representation(resource).success();
} else {
adminEvent.operation(operation).resourcePath(uriInfo).representation(resource).success();
}
}
}
}

View file

@ -25,11 +25,14 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.Constants;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.services.resources.admin.RealmAuth;
import javax.ws.rs.Consumes;
@ -41,9 +44,11 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import java.util.Arrays;
import java.util.HashMap;
@ -61,23 +66,27 @@ public class ScopeService {
private final AuthorizationProvider authorization;
private final RealmAuth auth;
private final AdminEventBuilder adminEvent;
private ResourceServer resourceServer;
public ScopeService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
public ScopeService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
this.resourceServer = resourceServer;
this.authorization = authorization;
this.auth = auth;
this.adminEvent = adminEvent.resource(ResourceType.AUTHORIZATION_SCOPE);
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response create(ScopeRepresentation scope) {
public Response create(@Context UriInfo uriInfo, ScopeRepresentation scope) {
this.auth.requireManage();
Scope model = toModel(scope, this.resourceServer, authorization);
scope.setId(model.getId());
audit(uriInfo, scope, scope.getId(), OperationType.CREATE);
return Response.status(Status.CREATED).entity(scope).build();
}
@ -85,7 +94,7 @@ public class ScopeService {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response update(@PathParam("id") String id, ScopeRepresentation scope) {
public Response update(@Context UriInfo uriInfo, @PathParam("id") String id, ScopeRepresentation scope) {
this.auth.requireManage();
scope.setId(id);
StoreFactory storeFactory = authorization.getStoreFactory();
@ -97,12 +106,14 @@ public class ScopeService {
toModel(scope, resourceServer, authorization);
audit(uriInfo, scope, OperationType.UPDATE);
return Response.noContent().build();
}
@Path("{id}")
@DELETE
public Response delete(@PathParam("id") String id) {
public Response delete(@Context UriInfo uriInfo, @PathParam("id") String id) {
this.auth.requireManage();
StoreFactory storeFactory = authorization.getStoreFactory();
List<Resource> resources = storeFactory.getResourceStore().findByScope(Arrays.asList(id), resourceServer.getId());
@ -130,6 +141,10 @@ public class ScopeService {
storeFactory.getScopeStore().delete(id);
if (authorization.getRealm().isAdminEventsEnabled()) {
audit(uriInfo, toRepresentation(scope), OperationType.DELETE);
}
return Response.noContent().build();
}
@ -144,7 +159,7 @@ public class ScopeService {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(toRepresentation(model, this.authorization)).build();
return Response.ok(toRepresentation(model)).build();
}
@Path("{id}/resources")
@ -212,22 +227,17 @@ public class ScopeService {
return Response.status(Status.OK).build();
}
return Response.ok(toRepresentation(model, authorization)).build();
return Response.ok(toRepresentation(model)).build();
}
@GET
@Produces("application/json")
public Response findAll(@QueryParam("scopeId") String id,
@QueryParam("name") String name,
@QueryParam("deep") Boolean deep,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResult) {
this.auth.requireView();
if (deep == null) {
deep = true;
}
Map<String, String[]> search = new HashMap<>();
if (id != null && !"".equals(id.trim())) {
@ -238,11 +248,24 @@ public class ScopeService {
search.put("name", new String[] {name});
}
Boolean finalDeep = deep;
return Response.ok(
this.authorization.getStoreFactory().getScopeStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
.map(scope -> toRepresentation(scope, this.authorization, finalDeep))
.map(scope -> toRepresentation(scope))
.collect(Collectors.toList()))
.build();
}
private void audit(@Context UriInfo uriInfo, ScopeRepresentation resource, OperationType operation) {
audit(uriInfo, resource, null, operation);
}
private void audit(@Context UriInfo uriInfo, ScopeRepresentation resource, String id, OperationType operation) {
if (authorization.getRealm().isAdminEventsEnabled()) {
if (id != null) {
adminEvent.operation(operation).resourcePath(uriInfo, id).representation(resource).success();
} else {
adminEvent.operation(operation).resourcePath(uriInfo).representation(resource).success();
}
}
}
}

View file

@ -27,12 +27,17 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.protection.permission.PermissionService;
import org.keycloak.authorization.protection.permission.PermissionsService;
import org.keycloak.authorization.protection.resource.ResourceService;
import org.keycloak.common.ClientConnection;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.resources.admin.AdminAuth;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response.Status;
/**
@ -41,6 +46,8 @@ import javax.ws.rs.core.Response.Status;
public class ProtectionService {
private final AuthorizationProvider authorization;
@Context
protected ClientConnection clientConnection;
public ProtectionService(AuthorizationProvider authorization) {
this.authorization = authorization;
@ -50,7 +57,12 @@ public class ProtectionService {
public Object resource() {
KeycloakIdentity identity = createIdentity();
ResourceServer resourceServer = getResourceServer(identity);
ResourceSetService resourceManager = new ResourceSetService(resourceServer, this.authorization, null);
RealmModel realm = authorization.getRealm();
ClientModel client = realm.getClientById(identity.getId());
KeycloakSession keycloakSession = authorization.getKeycloakSession();
UserModel serviceAccount = keycloakSession.users().getServiceAccount(client);
AdminEventBuilder adminEvent = new AdminEventBuilder(realm, new AdminAuth(realm, identity.getAccessToken(), serviceAccount, client), keycloakSession, clientConnection);
ResourceSetService resourceManager = new ResourceSetService(resourceServer, this.authorization, null, adminEvent.realm(realm).authClient(client).authUser(serviceAccount));
ResteasyProviderFactory.getInstance().injectProperties(resourceManager);

View file

@ -31,8 +31,10 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.admin.ResourceSetService;
@ -68,10 +70,10 @@ public class ResourceService {
@POST
@Consumes("application/json")
@Produces("application/json")
public Response create(UmaResourceRepresentation umaResource) {
public Response create(@Context UriInfo uriInfo, UmaResourceRepresentation umaResource) {
checkResourceServerSettings();
ResourceRepresentation resource = toResourceRepresentation(umaResource);
Response response = this.resourceManager.create(resource);
Response response = this.resourceManager.create(uriInfo, resource);
if (response.getEntity() instanceof ResourceRepresentation) {
return Response.status(Status.CREATED).entity(toUmaRepresentation((ResourceRepresentation) response.getEntity())).build();
@ -84,9 +86,9 @@ public class ResourceService {
@PUT
@Consumes("application/json")
@Produces("application/json")
public Response update(@PathParam("id") String id, UmaResourceRepresentation representation) {
public Response update(@Context UriInfo uriInfo, @PathParam("id") String id, UmaResourceRepresentation representation) {
ResourceRepresentation resource = toResourceRepresentation(representation);
Response response = this.resourceManager.update(id, resource);
Response response = this.resourceManager.update(uriInfo, id, resource);
if (response.getEntity() instanceof ResourceRepresentation) {
return Response.noContent().build();
@ -97,9 +99,9 @@ public class ResourceService {
@Path("/{id}")
@DELETE
public Response delete(@PathParam("id") String id) {
public Response delete(@Context UriInfo uriInfo, @PathParam("id") String id) {
checkResourceServerSettings();
return this.resourceManager.delete(id);
return this.resourceManager.delete(uriInfo, id);
}
@Path("/{id}")

View file

@ -343,7 +343,7 @@ public class ExportUtils {
representation.setPolicies(policies);
List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel.getId()).stream().map(scope -> {
ScopeRepresentation rep = toRepresentation(scope, authorization);
ScopeRepresentation rep = toRepresentation(scope);
rep.setId(null);
rep.setPolicies(null);

View file

@ -151,7 +151,7 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
}
private void configureAuthorizationSettings(KeycloakSession session, ClientModel client, ClientManager.InstallationAdapterConfig rep) {
if (new AuthorizationService(session, client, null).isEnabled()) {
if (new AuthorizationService(session, client, null, null).isEnabled()) {
PolicyEnforcerConfig enforcerConfig = new PolicyEnforcerConfig();
enforcerConfig.setEnforcementMode(null);

View file

@ -148,36 +148,13 @@ public class ClientResource {
try {
updateClientFromRep(rep, client, session);
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
updateAuthorizationSettings(rep);
return Response.noContent().build();
} catch (ModelDuplicateException e) {
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
}
}
public void updateClientFromRep(ClientRepresentation rep, ClientModel client, KeycloakSession session) throws ModelDuplicateException {
if (TRUE.equals(rep.isServiceAccountsEnabled())) {
UserModel serviceAccount = this.session.users().getServiceAccount(client);
if (serviceAccount == null) {
new ClientManager(new RealmManager(session)).enableServiceAccount(client);
}
}
if (!rep.getClientId().equals(client.getClientId())) {
new ClientManager(new RealmManager(session)).clientIdChanged(client, rep.getClientId());
}
RepresentationToModel.updateClient(rep, client);
if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) {
if (TRUE.equals(rep.getAuthorizationServicesEnabled())) {
authorization().enable();
} else {
authorization().disable();
}
}
}
/**
* Get representation of the client
*
@ -587,10 +564,36 @@ public class ClientResource {
public AuthorizationService authorization() {
ProfileHelper.requireFeature(Profile.Feature.AUTHORIZATION);
AuthorizationService resource = new AuthorizationService(this.session, this.client, this.auth);
AuthorizationService resource = new AuthorizationService(this.session, this.client, this.auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);
return resource;
}
private void updateClientFromRep(ClientRepresentation rep, ClientModel client, KeycloakSession session) throws ModelDuplicateException {
if (TRUE.equals(rep.isServiceAccountsEnabled())) {
UserModel serviceAccount = this.session.users().getServiceAccount(client);
if (serviceAccount == null) {
new ClientManager(new RealmManager(session)).enableServiceAccount(client);
}
}
if (!rep.getClientId().equals(client.getClientId())) {
new ClientManager(new RealmManager(session)).clientIdChanged(client, rep.getClientId());
}
RepresentationToModel.updateClient(rep, client);
}
private void updateAuthorizationSettings(ClientRepresentation rep) {
if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) {
if (TRUE.equals(rep.getAuthorizationServicesEnabled())) {
authorization().enable(false);
} else {
authorization().disable();
}
}
}
}

View file

@ -129,7 +129,7 @@ public class ClientsResource {
}
private AuthorizationService getAuthorizationService(ClientModel clientModel) {
return new AuthorizationService(session, clientModel, auth);
return new AuthorizationService(session, clientModel, auth, adminEvent);
}
/**
@ -167,14 +167,14 @@ public class ClientsResource {
}
}
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, clientModel.getId()).representation(rep).success();
if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) {
if (TRUE.equals(rep.getAuthorizationServicesEnabled())) {
getAuthorizationService(clientModel).enable();
getAuthorizationService(clientModel).enable(true);
}
}
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, clientModel.getId()).representation(rep).success();
return Response.created(uriInfo.getAbsolutePathBuilder().path(clientModel.getId()).build()).build();
} catch (ModelDuplicateException e) {
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");

View file

@ -107,7 +107,9 @@ public abstract class AbstractClientTest extends AbstractAuthTest {
clientRep.setPublicClient(Boolean.FALSE);
clientRep.setAuthorizationServicesEnabled(Boolean.TRUE);
clientRep.setServiceAccountsEnabled(Boolean.TRUE);
return createClient(clientRep);
String id = createClient(clientRep);
assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientResourcePath(id), ResourceType.AUTHORIZATION_RESOURCE_SERVER);
return id;
}
protected ClientRepresentation createOidcClientRep(String name) {