Merge pull request #4214 from pedroigor/KEYCLOAK-4904
[KEYCLOAK-4904] - Authorization Audit - Part 1
This commit is contained in:
commit
fd8a3dccaf
18 changed files with 254 additions and 95 deletions
|
@ -156,5 +156,25 @@ public enum ResourceType {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
, COMPONENT;
|
, COMPONENT
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
, AUTHORIZATION_RESOURCE_SERVER
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
, AUTHORIZATION_RESOURCE
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
, AUTHORIZATION_SCOPE
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
, AUTHORIZATION_POLICY;
|
||||||
}
|
}
|
||||||
|
|
|
@ -772,11 +772,7 @@ public class ModelToRepresentation {
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider) {
|
public static ScopeRepresentation toRepresentation(Scope model) {
|
||||||
return toRepresentation(model, authorizationProvider, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider, boolean deep) {
|
|
||||||
ScopeRepresentation scope = new ScopeRepresentation();
|
ScopeRepresentation scope = new ScopeRepresentation();
|
||||||
|
|
||||||
scope.setId(model.getId());
|
scope.setId(model.getId());
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.RealmAuth;
|
import org.keycloak.services.resources.admin.RealmAuth;
|
||||||
|
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
@ -34,14 +35,14 @@ public class AuthorizationService {
|
||||||
|
|
||||||
private final RealmAuth auth;
|
private final RealmAuth auth;
|
||||||
private final ClientModel client;
|
private final ClientModel client;
|
||||||
private final KeycloakSession session;
|
|
||||||
private final ResourceServer resourceServer;
|
private final ResourceServer resourceServer;
|
||||||
private final AuthorizationProvider authorization;
|
private final AuthorizationProvider authorization;
|
||||||
|
private final AdminEventBuilder adminEvent;
|
||||||
|
|
||||||
public AuthorizationService(KeycloakSession session, ClientModel client, RealmAuth auth) {
|
public AuthorizationService(KeycloakSession session, ClientModel client, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||||
this.session = session;
|
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.authorization = session.getProvider(AuthorizationProvider.class);
|
this.authorization = session.getProvider(AuthorizationProvider.class);
|
||||||
|
this.adminEvent = adminEvent;
|
||||||
this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().findByClient(this.client.getId());
|
this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().findByClient(this.client.getId());
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
|
|
||||||
|
@ -52,16 +53,16 @@ public class AuthorizationService {
|
||||||
|
|
||||||
@Path("/resource-server")
|
@Path("/resource-server")
|
||||||
public ResourceServerService resourceServer() {
|
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);
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enable() {
|
public void enable(boolean newClient) {
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
resourceServer().create();
|
resourceServer().create(newClient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.Map;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.model.Policy;
|
import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.RealmAuth;
|
import org.keycloak.services.resources.admin.RealmAuth;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,18 +30,18 @@ import org.keycloak.services.resources.admin.RealmAuth;
|
||||||
*/
|
*/
|
||||||
public class PermissionService extends PolicyService {
|
public class PermissionService extends PolicyService {
|
||||||
|
|
||||||
public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
|
public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||||
super(resourceServer, authorization, auth);
|
super(resourceServer, authorization, auth, adminEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PolicyResourceService doCreatePolicyResource(Policy policy) {
|
protected PolicyResourceService doCreatePolicyResource(Policy policy) {
|
||||||
return new PolicyTypeResourceService(policy, resourceServer, authorization, auth);
|
return new PolicyTypeResourceService(policy, resourceServer, authorization, auth, adminEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PolicyTypeService doCreatePolicyTypeResource(String type) {
|
protected PolicyTypeService doCreatePolicyTypeResource(String type) {
|
||||||
return new PolicyTypeService(type, resourceServer, authorization, auth) {
|
return new PolicyTypeService(type, resourceServer, authorization, auth, adminEvent) {
|
||||||
@Override
|
@Override
|
||||||
protected List<Object> doSearch(Integer firstResult, Integer maxResult, Map<String, String[]> filters) {
|
protected List<Object> doSearch(Integer firstResult, Integer maxResult, Map<String, String[]> filters) {
|
||||||
filters.put("permission", new String[] {Boolean.TRUE.toString()});
|
filters.put("permission", new String[] {Boolean.TRUE.toString()});
|
||||||
|
|
|
@ -26,8 +26,10 @@ import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.PUT;
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
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.policy.provider.PolicyProviderFactory;
|
||||||
import org.keycloak.authorization.store.PolicyStore;
|
import org.keycloak.authorization.store.PolicyStore;
|
||||||
import org.keycloak.authorization.store.StoreFactory;
|
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.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.RealmAuth;
|
import org.keycloak.services.resources.admin.RealmAuth;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -54,19 +59,21 @@ public class PolicyResourceService {
|
||||||
protected final ResourceServer resourceServer;
|
protected final ResourceServer resourceServer;
|
||||||
protected final AuthorizationProvider authorization;
|
protected final AuthorizationProvider authorization;
|
||||||
protected final RealmAuth auth;
|
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.policy = policy;
|
||||||
this.resourceServer = resourceServer;
|
this.resourceServer = resourceServer;
|
||||||
this.authorization = authorization;
|
this.authorization = authorization;
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
|
this.adminEvent = adminEvent.resource(ResourceType.AUTHORIZATION_POLICY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Consumes("application/json")
|
@Consumes("application/json")
|
||||||
@Produces("application/json")
|
@Produces("application/json")
|
||||||
@NoCache
|
@NoCache
|
||||||
public Response update(String payload) {
|
public Response update(@Context UriInfo uriInfo, String payload) {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
|
|
||||||
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
|
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
|
||||||
|
@ -79,11 +86,14 @@ public class PolicyResourceService {
|
||||||
|
|
||||||
RepresentationToModel.toModel(representation, authorization, policy);
|
RepresentationToModel.toModel(representation, authorization, policy);
|
||||||
|
|
||||||
|
|
||||||
|
audit(uriInfo, representation, OperationType.UPDATE);
|
||||||
|
|
||||||
return Response.status(Status.CREATED).build();
|
return Response.status(Status.CREATED).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
public Response delete() {
|
public Response delete(@Context UriInfo uriInfo) {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
|
|
||||||
if (policy == null) {
|
if (policy == null) {
|
||||||
|
@ -98,6 +108,10 @@ public class PolicyResourceService {
|
||||||
|
|
||||||
policyStore.delete(policy.getId());
|
policyStore.delete(policy.getId());
|
||||||
|
|
||||||
|
if (authorization.getRealm().isAdminEventsEnabled()) {
|
||||||
|
audit(uriInfo, toRepresentation(policy, authorization), OperationType.DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,4 +239,10 @@ public class PolicyResourceService {
|
||||||
protected Policy getPolicy() {
|
protected Policy getPolicy() {
|
||||||
return policy;
|
return policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void audit(@Context UriInfo uriInfo, AbstractPolicyRepresentation policy, OperationType operation) {
|
||||||
|
if (authorization.getRealm().isAdminEventsEnabled()) {
|
||||||
|
adminEvent.operation(operation).resourcePath(uriInfo).representation(policy).success();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,11 @@ import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
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.policy.provider.PolicyProviderFactory;
|
||||||
import org.keycloak.authorization.store.PolicyStore;
|
import org.keycloak.authorization.store.PolicyStore;
|
||||||
import org.keycloak.authorization.store.StoreFactory;
|
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.Constants;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
|
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||||
import org.keycloak.services.ErrorResponseException;
|
import org.keycloak.services.ErrorResponseException;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.RealmAuth;
|
import org.keycloak.services.resources.admin.RealmAuth;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -63,11 +69,13 @@ public class PolicyService {
|
||||||
protected final ResourceServer resourceServer;
|
protected final ResourceServer resourceServer;
|
||||||
protected final AuthorizationProvider authorization;
|
protected final AuthorizationProvider authorization;
|
||||||
protected final RealmAuth auth;
|
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.resourceServer = resourceServer;
|
||||||
this.authorization = authorization;
|
this.authorization = authorization;
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
|
this.adminEvent = adminEvent.resource(ResourceType.AUTHORIZATION_POLICY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("{type}")
|
@Path("{type}")
|
||||||
|
@ -84,18 +92,18 @@ public class PolicyService {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PolicyTypeService doCreatePolicyTypeResource(String type) {
|
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) {
|
protected Object doCreatePolicyResource(Policy policy) {
|
||||||
return new PolicyResourceService(policy, resourceServer, authorization, auth);
|
return new PolicyResourceService(policy, resourceServer, authorization, auth, adminEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@NoCache
|
@NoCache
|
||||||
public Response create(String payload) {
|
public Response create(@Context UriInfo uriInfo, String payload) {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
|
|
||||||
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
|
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
|
||||||
|
@ -103,6 +111,8 @@ public class PolicyService {
|
||||||
|
|
||||||
representation.setId(policy.getId());
|
representation.setId(policy.getId());
|
||||||
|
|
||||||
|
audit(uriInfo, representation, representation.getId(), OperationType.CREATE);
|
||||||
|
|
||||||
return Response.status(Status.CREATED).entity(representation).build();
|
return Response.status(Status.CREATED).entity(representation).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,4 +290,14 @@ public class PolicyService {
|
||||||
findAssociatedPolicies(associated, policies);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.RealmAuth;
|
import org.keycloak.services.resources.admin.RealmAuth;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -32,8 +33,8 @@ import org.keycloak.util.JsonSerialization;
|
||||||
*/
|
*/
|
||||||
public class PolicyTypeResourceService extends PolicyResourceService {
|
public class PolicyTypeResourceService extends PolicyResourceService {
|
||||||
|
|
||||||
public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
|
public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||||
super(policy, resourceServer, authorization, auth);
|
super(policy, resourceServer, authorization, auth, adminEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.RealmAuth;
|
import org.keycloak.services.resources.admin.RealmAuth;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -40,8 +41,8 @@ public class PolicyTypeService extends PolicyService {
|
||||||
|
|
||||||
private final String type;
|
private final String type;
|
||||||
|
|
||||||
PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
|
PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||||
super(resourceServer, authorization, auth);
|
super(resourceServer, authorization, auth, adminEvent);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ public class PolicyTypeService extends PolicyService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object doCreatePolicyResource(Policy policy) {
|
protected Object doCreatePolicyResource(Policy policy) {
|
||||||
return new PolicyTypeResourceService(policy, resourceServer,authorization, auth);
|
return new PolicyTypeResourceService(policy, resourceServer,authorization, auth, adminEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -40,12 +40,15 @@ import org.keycloak.authorization.store.PolicyStore;
|
||||||
import org.keycloak.authorization.store.ResourceStore;
|
import org.keycloak.authorization.store.ResourceStore;
|
||||||
import org.keycloak.authorization.store.ScopeStore;
|
import org.keycloak.authorization.store.ScopeStore;
|
||||||
import org.keycloak.authorization.store.StoreFactory;
|
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.exportimport.util.ExportUtils;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||||
import org.keycloak.representations.idm.authorization.Logic;
|
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.ResourcePermissionRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.RealmAuth;
|
import org.keycloak.services.resources.admin.RealmAuth;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,19 +66,24 @@ public class ResourceServerService {
|
||||||
|
|
||||||
private final AuthorizationProvider authorization;
|
private final AuthorizationProvider authorization;
|
||||||
private final RealmAuth auth;
|
private final RealmAuth auth;
|
||||||
|
private final AdminEventBuilder adminEvent;
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
private ResourceServer resourceServer;
|
private ResourceServer resourceServer;
|
||||||
private final ClientModel client;
|
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.authorization = authorization;
|
||||||
this.session = authorization.getKeycloakSession();
|
this.session = authorization.getKeycloakSession();
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.resourceServer = resourceServer;
|
this.resourceServer = resourceServer;
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
|
this.adminEvent = adminEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void create() {
|
public void create(boolean newClient) {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
|
|
||||||
UserModel serviceAccount = this.session.users().getServiceAccount(client);
|
UserModel serviceAccount = this.session.users().getServiceAccount(client);
|
||||||
|
@ -86,16 +95,17 @@ public class ResourceServerService {
|
||||||
this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().create(this.client.getId());
|
this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().create(this.client.getId());
|
||||||
createDefaultRoles(serviceAccount);
|
createDefaultRoles(serviceAccount);
|
||||||
createDefaultPermission(createDefaultResource(), createDefaultPolicy());
|
createDefaultPermission(createDefaultResource(), createDefaultPolicy());
|
||||||
|
audit(OperationType.CREATE, uriInfo, newClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Consumes("application/json")
|
@Consumes("application/json")
|
||||||
@Produces("application/json")
|
@Produces("application/json")
|
||||||
public Response update(ResourceServerRepresentation server) {
|
public Response update(@Context UriInfo uriInfo, ResourceServerRepresentation server) {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
this.resourceServer.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement());
|
this.resourceServer.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement());
|
||||||
this.resourceServer.setPolicyEnforcementMode(server.getPolicyEnforcementMode());
|
this.resourceServer.setPolicyEnforcementMode(server.getPolicyEnforcementMode());
|
||||||
|
audit(OperationType.UPDATE, uriInfo, false);
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,17 +115,19 @@ public class ResourceServerService {
|
||||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||||
String id = resourceServer.getId();
|
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()));
|
resourceStore.findByResourceServer(id).forEach(resource -> resourceStore.delete(resource.getId()));
|
||||||
|
|
||||||
ScopeStore scopeStore = storeFactory.getScopeStore();
|
ScopeStore scopeStore = storeFactory.getScopeStore();
|
||||||
|
|
||||||
scopeStore.findByResourceServer(id).forEach(scope -> scopeStore.delete(scope.getId()));
|
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);
|
storeFactory.getResourceServerStore().delete(id);
|
||||||
|
|
||||||
|
audit(OperationType.DELETE, uriInfo, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -143,12 +155,14 @@ public class ResourceServerService {
|
||||||
|
|
||||||
RepresentationToModel.toModel(rep, authorization);
|
RepresentationToModel.toModel(rep, authorization);
|
||||||
|
|
||||||
|
audit(OperationType.UPDATE, uriInfo, false);
|
||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("/resource")
|
@Path("/resource")
|
||||||
public ResourceSetService getResourceSetResource() {
|
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);
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
|
||||||
|
@ -157,7 +171,7 @@ public class ResourceServerService {
|
||||||
|
|
||||||
@Path("/scope")
|
@Path("/scope")
|
||||||
public ScopeService getScopeResource() {
|
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);
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
|
||||||
|
@ -166,7 +180,7 @@ public class ResourceServerService {
|
||||||
|
|
||||||
@Path("/policy")
|
@Path("/policy")
|
||||||
public PolicyService getPolicyResource() {
|
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);
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
|
||||||
|
@ -176,7 +190,7 @@ public class ResourceServerService {
|
||||||
@Path("/permission")
|
@Path("/permission")
|
||||||
public Object getPermissionTypeResource() {
|
public Object getPermissionTypeResource() {
|
||||||
this.auth.requireView();
|
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);
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
|
||||||
|
@ -239,4 +253,14 @@ public class ResourceServerService {
|
||||||
serviceAccount.grantRole(umaProtectionRole);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ import org.keycloak.authorization.model.Scope;
|
||||||
import org.keycloak.authorization.store.PolicyStore;
|
import org.keycloak.authorization.store.PolicyStore;
|
||||||
import org.keycloak.authorization.store.ResourceStore;
|
import org.keycloak.authorization.store.ResourceStore;
|
||||||
import org.keycloak.authorization.store.StoreFactory;
|
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.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
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.ResourceRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.RealmAuth;
|
import org.keycloak.services.resources.admin.RealmAuth;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
|
@ -48,8 +51,10 @@ import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -70,17 +75,25 @@ public class ResourceSetService {
|
||||||
|
|
||||||
private final AuthorizationProvider authorization;
|
private final AuthorizationProvider authorization;
|
||||||
private final RealmAuth auth;
|
private final RealmAuth auth;
|
||||||
|
private final AdminEventBuilder adminEvent;
|
||||||
private ResourceServer resourceServer;
|
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.resourceServer = resourceServer;
|
||||||
this.authorization = authorization;
|
this.authorization = authorization;
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
|
this.adminEvent = adminEvent.resource(ResourceType.AUTHORIZATION_RESOURCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Consumes("application/json")
|
@Consumes("application/json")
|
||||||
@Produces("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) {
|
public Response create(ResourceRepresentation resource) {
|
||||||
requireManage();
|
requireManage();
|
||||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||||
|
@ -128,7 +141,7 @@ public class ResourceSetService {
|
||||||
@PUT
|
@PUT
|
||||||
@Consumes("application/json")
|
@Consumes("application/json")
|
||||||
@Produces("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();
|
requireManage();
|
||||||
resource.setId(id);
|
resource.setId(id);
|
||||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||||
|
@ -141,12 +154,14 @@ public class ResourceSetService {
|
||||||
|
|
||||||
toModel(resource, resourceServer, authorization);
|
toModel(resource, resourceServer, authorization);
|
||||||
|
|
||||||
|
audit(uriInfo, resource, OperationType.UPDATE);
|
||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
@DELETE
|
@DELETE
|
||||||
public Response delete(@PathParam("id") String id) {
|
public Response delete(@Context UriInfo uriInfo, @PathParam("id") String id) {
|
||||||
requireManage();
|
requireManage();
|
||||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
Resource resource = storeFactory.getResourceStore().findById(id, resourceServer.getId());
|
Resource resource = storeFactory.getResourceStore().findById(id, resourceServer.getId());
|
||||||
|
@ -168,6 +183,10 @@ public class ResourceSetService {
|
||||||
|
|
||||||
storeFactory.getResourceStore().delete(id);
|
storeFactory.getResourceStore().delete(id);
|
||||||
|
|
||||||
|
if (authorization.getRealm().isAdminEventsEnabled()) {
|
||||||
|
audit(uriInfo, toRepresentation(resource, resourceServer, authorization), OperationType.DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,4 +395,18 @@ public class ResourceSetService {
|
||||||
this.auth.requireManage();
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -25,11 +25,14 @@ import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
import org.keycloak.authorization.store.PolicyStore;
|
import org.keycloak.authorization.store.PolicyStore;
|
||||||
import org.keycloak.authorization.store.StoreFactory;
|
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.Constants;
|
||||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.RealmAuth;
|
import org.keycloak.services.resources.admin.RealmAuth;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
|
@ -41,9 +44,11 @@ import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -61,23 +66,27 @@ public class ScopeService {
|
||||||
|
|
||||||
private final AuthorizationProvider authorization;
|
private final AuthorizationProvider authorization;
|
||||||
private final RealmAuth auth;
|
private final RealmAuth auth;
|
||||||
|
private final AdminEventBuilder adminEvent;
|
||||||
private ResourceServer resourceServer;
|
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.resourceServer = resourceServer;
|
||||||
this.authorization = authorization;
|
this.authorization = authorization;
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
|
this.adminEvent = adminEvent.resource(ResourceType.AUTHORIZATION_SCOPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Response create(ScopeRepresentation scope) {
|
public Response create(@Context UriInfo uriInfo, ScopeRepresentation scope) {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
Scope model = toModel(scope, this.resourceServer, authorization);
|
Scope model = toModel(scope, this.resourceServer, authorization);
|
||||||
|
|
||||||
scope.setId(model.getId());
|
scope.setId(model.getId());
|
||||||
|
|
||||||
|
audit(uriInfo, scope, scope.getId(), OperationType.CREATE);
|
||||||
|
|
||||||
return Response.status(Status.CREATED).entity(scope).build();
|
return Response.status(Status.CREATED).entity(scope).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +94,7 @@ public class ScopeService {
|
||||||
@PUT
|
@PUT
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Produces(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();
|
this.auth.requireManage();
|
||||||
scope.setId(id);
|
scope.setId(id);
|
||||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
|
@ -97,12 +106,14 @@ public class ScopeService {
|
||||||
|
|
||||||
toModel(scope, resourceServer, authorization);
|
toModel(scope, resourceServer, authorization);
|
||||||
|
|
||||||
|
audit(uriInfo, scope, OperationType.UPDATE);
|
||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
@DELETE
|
@DELETE
|
||||||
public Response delete(@PathParam("id") String id) {
|
public Response delete(@Context UriInfo uriInfo, @PathParam("id") String id) {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
List<Resource> resources = storeFactory.getResourceStore().findByScope(Arrays.asList(id), resourceServer.getId());
|
List<Resource> resources = storeFactory.getResourceStore().findByScope(Arrays.asList(id), resourceServer.getId());
|
||||||
|
@ -130,6 +141,10 @@ public class ScopeService {
|
||||||
|
|
||||||
storeFactory.getScopeStore().delete(id);
|
storeFactory.getScopeStore().delete(id);
|
||||||
|
|
||||||
|
if (authorization.getRealm().isAdminEventsEnabled()) {
|
||||||
|
audit(uriInfo, toRepresentation(scope), OperationType.DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +159,7 @@ public class ScopeService {
|
||||||
return Response.status(Status.NOT_FOUND).build();
|
return Response.status(Status.NOT_FOUND).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.ok(toRepresentation(model, this.authorization)).build();
|
return Response.ok(toRepresentation(model)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("{id}/resources")
|
@Path("{id}/resources")
|
||||||
|
@ -212,22 +227,17 @@ public class ScopeService {
|
||||||
return Response.status(Status.OK).build();
|
return Response.status(Status.OK).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.ok(toRepresentation(model, authorization)).build();
|
return Response.ok(toRepresentation(model)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces("application/json")
|
@Produces("application/json")
|
||||||
public Response findAll(@QueryParam("scopeId") String id,
|
public Response findAll(@QueryParam("scopeId") String id,
|
||||||
@QueryParam("name") String name,
|
@QueryParam("name") String name,
|
||||||
@QueryParam("deep") Boolean deep,
|
|
||||||
@QueryParam("first") Integer firstResult,
|
@QueryParam("first") Integer firstResult,
|
||||||
@QueryParam("max") Integer maxResult) {
|
@QueryParam("max") Integer maxResult) {
|
||||||
this.auth.requireView();
|
this.auth.requireView();
|
||||||
|
|
||||||
if (deep == null) {
|
|
||||||
deep = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, String[]> search = new HashMap<>();
|
Map<String, String[]> search = new HashMap<>();
|
||||||
|
|
||||||
if (id != null && !"".equals(id.trim())) {
|
if (id != null && !"".equals(id.trim())) {
|
||||||
|
@ -238,11 +248,24 @@ public class ScopeService {
|
||||||
search.put("name", new String[] {name});
|
search.put("name", new String[] {name});
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean finalDeep = deep;
|
|
||||||
return Response.ok(
|
return Response.ok(
|
||||||
this.authorization.getStoreFactory().getScopeStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
|
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()))
|
.collect(Collectors.toList()))
|
||||||
.build();
|
.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,17 @@ import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.protection.permission.PermissionService;
|
import org.keycloak.authorization.protection.permission.PermissionService;
|
||||||
import org.keycloak.authorization.protection.permission.PermissionsService;
|
import org.keycloak.authorization.protection.permission.PermissionsService;
|
||||||
import org.keycloak.authorization.protection.resource.ResourceService;
|
import org.keycloak.authorization.protection.resource.ResourceService;
|
||||||
|
import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.services.ErrorResponseException;
|
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.Path;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,6 +46,8 @@ import javax.ws.rs.core.Response.Status;
|
||||||
public class ProtectionService {
|
public class ProtectionService {
|
||||||
|
|
||||||
private final AuthorizationProvider authorization;
|
private final AuthorizationProvider authorization;
|
||||||
|
@Context
|
||||||
|
protected ClientConnection clientConnection;
|
||||||
|
|
||||||
public ProtectionService(AuthorizationProvider authorization) {
|
public ProtectionService(AuthorizationProvider authorization) {
|
||||||
this.authorization = authorization;
|
this.authorization = authorization;
|
||||||
|
@ -50,7 +57,12 @@ public class ProtectionService {
|
||||||
public Object resource() {
|
public Object resource() {
|
||||||
KeycloakIdentity identity = createIdentity();
|
KeycloakIdentity identity = createIdentity();
|
||||||
ResourceServer resourceServer = getResourceServer(identity);
|
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);
|
ResteasyProviderFactory.getInstance().injectProperties(resourceManager);
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,10 @@ import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.admin.ResourceSetService;
|
import org.keycloak.authorization.admin.ResourceSetService;
|
||||||
|
@ -68,10 +70,10 @@ public class ResourceService {
|
||||||
@POST
|
@POST
|
||||||
@Consumes("application/json")
|
@Consumes("application/json")
|
||||||
@Produces("application/json")
|
@Produces("application/json")
|
||||||
public Response create(UmaResourceRepresentation umaResource) {
|
public Response create(@Context UriInfo uriInfo, UmaResourceRepresentation umaResource) {
|
||||||
checkResourceServerSettings();
|
checkResourceServerSettings();
|
||||||
ResourceRepresentation resource = toResourceRepresentation(umaResource);
|
ResourceRepresentation resource = toResourceRepresentation(umaResource);
|
||||||
Response response = this.resourceManager.create(resource);
|
Response response = this.resourceManager.create(uriInfo, resource);
|
||||||
|
|
||||||
if (response.getEntity() instanceof ResourceRepresentation) {
|
if (response.getEntity() instanceof ResourceRepresentation) {
|
||||||
return Response.status(Status.CREATED).entity(toUmaRepresentation((ResourceRepresentation) response.getEntity())).build();
|
return Response.status(Status.CREATED).entity(toUmaRepresentation((ResourceRepresentation) response.getEntity())).build();
|
||||||
|
@ -84,9 +86,9 @@ public class ResourceService {
|
||||||
@PUT
|
@PUT
|
||||||
@Consumes("application/json")
|
@Consumes("application/json")
|
||||||
@Produces("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);
|
ResourceRepresentation resource = toResourceRepresentation(representation);
|
||||||
Response response = this.resourceManager.update(id, resource);
|
Response response = this.resourceManager.update(uriInfo, id, resource);
|
||||||
|
|
||||||
if (response.getEntity() instanceof ResourceRepresentation) {
|
if (response.getEntity() instanceof ResourceRepresentation) {
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
|
@ -97,9 +99,9 @@ public class ResourceService {
|
||||||
|
|
||||||
@Path("/{id}")
|
@Path("/{id}")
|
||||||
@DELETE
|
@DELETE
|
||||||
public Response delete(@PathParam("id") String id) {
|
public Response delete(@Context UriInfo uriInfo, @PathParam("id") String id) {
|
||||||
checkResourceServerSettings();
|
checkResourceServerSettings();
|
||||||
return this.resourceManager.delete(id);
|
return this.resourceManager.delete(uriInfo, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("/{id}")
|
@Path("/{id}")
|
||||||
|
|
|
@ -343,7 +343,7 @@ public class ExportUtils {
|
||||||
representation.setPolicies(policies);
|
representation.setPolicies(policies);
|
||||||
|
|
||||||
List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel.getId()).stream().map(scope -> {
|
List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel.getId()).stream().map(scope -> {
|
||||||
ScopeRepresentation rep = toRepresentation(scope, authorization);
|
ScopeRepresentation rep = toRepresentation(scope);
|
||||||
|
|
||||||
rep.setId(null);
|
rep.setId(null);
|
||||||
rep.setPolicies(null);
|
rep.setPolicies(null);
|
||||||
|
|
|
@ -151,7 +151,7 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureAuthorizationSettings(KeycloakSession session, ClientModel client, ClientManager.InstallationAdapterConfig rep) {
|
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();
|
PolicyEnforcerConfig enforcerConfig = new PolicyEnforcerConfig();
|
||||||
|
|
||||||
enforcerConfig.setEnforcementMode(null);
|
enforcerConfig.setEnforcementMode(null);
|
||||||
|
|
|
@ -148,36 +148,13 @@ public class ClientResource {
|
||||||
try {
|
try {
|
||||||
updateClientFromRep(rep, client, session);
|
updateClientFromRep(rep, client, session);
|
||||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
|
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
|
||||||
|
updateAuthorizationSettings(rep);
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (ModelDuplicateException e) {
|
} catch (ModelDuplicateException e) {
|
||||||
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
|
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
|
* Get representation of the client
|
||||||
*
|
*
|
||||||
|
@ -587,10 +564,36 @@ public class ClientResource {
|
||||||
public AuthorizationService authorization() {
|
public AuthorizationService authorization() {
|
||||||
ProfileHelper.requireFeature(Profile.Feature.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);
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
|
||||||
return 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ public class ClientsResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthorizationService getAuthorizationService(ClientModel clientModel) {
|
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 (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) {
|
||||||
if (TRUE.equals(rep.getAuthorizationServicesEnabled())) {
|
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();
|
return Response.created(uriInfo.getAbsolutePathBuilder().path(clientModel.getId()).build()).build();
|
||||||
} catch (ModelDuplicateException e) {
|
} catch (ModelDuplicateException e) {
|
||||||
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
|
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
|
||||||
|
|
|
@ -107,7 +107,9 @@ public abstract class AbstractClientTest extends AbstractAuthTest {
|
||||||
clientRep.setPublicClient(Boolean.FALSE);
|
clientRep.setPublicClient(Boolean.FALSE);
|
||||||
clientRep.setAuthorizationServicesEnabled(Boolean.TRUE);
|
clientRep.setAuthorizationServicesEnabled(Boolean.TRUE);
|
||||||
clientRep.setServiceAccountsEnabled(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) {
|
protected ClientRepresentation createOidcClientRep(String name) {
|
||||||
|
|
Loading…
Reference in a new issue