diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java index 47b4db4a01..3c8552efe1 100755 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java @@ -46,6 +46,10 @@ public interface ClientsResource { @Produces(MediaType.APPLICATION_JSON) public List findAll(); + @GET + @Produces(MediaType.APPLICATION_JSON) + public List findAll(@QueryParam("viewableOnly") boolean viewableOnly); + @GET @Produces(MediaType.APPLICATION_JSON) public List findByClientId(@QueryParam("clientId") String clientId); diff --git a/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java b/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java index a4ff868276..d062856e2e 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java @@ -23,7 +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.RealmAuth; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.Path; @@ -32,22 +32,18 @@ import javax.ws.rs.Path; */ public class AuthorizationService { - private final RealmAuth auth; + private final AdminPermissionEvaluator auth; private final ClientModel client; private final KeycloakSession session; private final ResourceServer resourceServer; private final AuthorizationProvider authorization; - public AuthorizationService(KeycloakSession session, ClientModel client, RealmAuth auth) { + public AuthorizationService(KeycloakSession session, ClientModel client, AdminPermissionEvaluator auth) { this.session = session; this.client = client; this.authorization = session.getProvider(AuthorizationProvider.class); this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().findByClient(this.client.getId()); this.auth = auth; - - if (auth != null) { - this.auth.init(RealmAuth.Resource.AUTHORIZATION); - } } @Path("/resource-server") diff --git a/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java b/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java index 4ada87d316..e7f463302f 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java @@ -22,14 +22,14 @@ 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.RealmAuth; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; /** * @author Pedro Igor */ public class PermissionService extends PolicyService { - public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { + public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth) { super(resourceServer, authorization, auth); } diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java index 24e1a8943e..0a913dda03 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java @@ -63,14 +63,12 @@ import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.protocol.ProtocolMapper; -import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper; import org.keycloak.representations.AccessToken; import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.services.Urls; import org.keycloak.services.managers.AuthenticationManager; -import org.keycloak.services.resources.admin.RealmAuth; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.sessions.AuthenticationSessionModel; /** @@ -79,13 +77,13 @@ import org.keycloak.sessions.AuthenticationSessionModel; public class PolicyEvaluationService { private final AuthorizationProvider authorization; - private final RealmAuth auth; + private final AdminPermissionEvaluator auth; @Context private HttpRequest httpRequest; private final ResourceServer resourceServer; - PolicyEvaluationService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { + PolicyEvaluationService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth) { this.resourceServer = resourceServer; this.authorization = authorization; this.auth = auth; @@ -117,7 +115,7 @@ public class PolicyEvaluationService { @Consumes("application/json") @Produces("application/json") public Response evaluate(PolicyEvaluationRequest evaluationRequest) throws Throwable { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); CloseableKeycloakIdentity identity = createIdentity(evaluationRequest); try { EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity); diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java index 85e0943221..51bc2a6e6f 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java @@ -42,7 +42,7 @@ import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentati 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.RealmAuth; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.util.JsonSerialization; /** @@ -53,9 +53,9 @@ public class PolicyResourceService { private final Policy policy; protected final ResourceServer resourceServer; protected final AuthorizationProvider authorization; - protected final RealmAuth auth; + protected final AdminPermissionEvaluator auth; - public PolicyResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { + public PolicyResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth) { this.policy = policy; this.resourceServer = resourceServer; this.authorization = authorization; @@ -67,7 +67,7 @@ public class PolicyResourceService { @Produces("application/json") @NoCache public Response update(String payload) { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); AbstractPolicyRepresentation representation = doCreateRepresentation(payload); @@ -84,7 +84,7 @@ public class PolicyResourceService { @DELETE public Response delete() { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); if (policy == null) { return Response.status(Status.NOT_FOUND).build(); @@ -105,7 +105,7 @@ public class PolicyResourceService { @Produces("application/json") @NoCache public Response findById() { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); if (policy == null) { return Response.status(Status.NOT_FOUND).build(); @@ -123,7 +123,7 @@ public class PolicyResourceService { @Produces("application/json") @NoCache public Response getDependentPolicies() { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); if (policy == null) { return Response.status(Status.NOT_FOUND).build(); @@ -147,7 +147,7 @@ public class PolicyResourceService { @Produces("application/json") @NoCache public Response getScopes() { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); if (policy == null) { return Response.status(Status.NOT_FOUND).build(); @@ -168,7 +168,7 @@ public class PolicyResourceService { @Produces("application/json") @NoCache public Response getResources() { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); if (policy == null) { return Response.status(Status.NOT_FOUND).build(); @@ -189,7 +189,7 @@ public class PolicyResourceService { @Produces("application/json") @NoCache public Response getAssociatedPolicies() { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); if (policy == null) { return Response.status(Status.NOT_FOUND).build(); diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java index 011aa2d3a1..6ebed116c9 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java @@ -52,7 +52,7 @@ import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentati import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation; import org.keycloak.representations.idm.authorization.PolicyRepresentation; import org.keycloak.services.ErrorResponseException; -import org.keycloak.services.resources.admin.RealmAuth; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.util.JsonSerialization; /** @@ -62,9 +62,9 @@ public class PolicyService { protected final ResourceServer resourceServer; protected final AuthorizationProvider authorization; - protected final RealmAuth auth; + protected final AdminPermissionEvaluator auth; - public PolicyService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { + public PolicyService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth) { this.resourceServer = resourceServer; this.authorization = authorization; this.auth = auth; @@ -92,7 +92,7 @@ public class PolicyService { @Produces(MediaType.APPLICATION_JSON) @NoCache public Response create(String payload) { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); AbstractPolicyRepresentation representation = doCreateRepresentation(payload); Policy policy = create(representation); @@ -130,7 +130,7 @@ public class PolicyService { @Produces(MediaType.APPLICATION_JSON) @NoCache public Response findByName(@QueryParam("name") String name) { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); StoreFactory storeFactory = authorization.getStoreFactory(); if (name == null) { @@ -157,7 +157,7 @@ public class PolicyService { @QueryParam("permission") Boolean permission, @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResult) { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); Map search = new HashMap<>(); @@ -236,7 +236,7 @@ public class PolicyService { @Produces(MediaType.APPLICATION_JSON) @NoCache public Response findPolicyProviders() { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); return Response.ok( authorization.getProviderFactories().stream() .map(provider -> { @@ -254,7 +254,7 @@ public class PolicyService { @Path("evaluate") public PolicyEvaluationService getPolicyEvaluateResource() { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); PolicyEvaluationService resource = new PolicyEvaluationService(this.resourceServer, this.authorization, this.auth); ResteasyProviderFactory.getInstance().injectProperties(resource); diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java index 8756721e9d..71a66956c0 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java @@ -24,7 +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.RealmAuth; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.util.JsonSerialization; /** @@ -32,7 +32,7 @@ import org.keycloak.util.JsonSerialization; */ public class PolicyTypeResourceService extends PolicyResourceService { - public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { + public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth) { super(policy, resourceServer, authorization, auth); } diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java index c2e4db5c55..d6868c5e5d 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java @@ -28,7 +28,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.RealmAuth; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.util.JsonSerialization; /** @@ -38,7 +38,7 @@ public class PolicyTypeService extends PolicyService { private final String type; - PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { + PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth) { super(resourceServer, authorization, auth); this.type = type; } diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java index 15f1db7a52..a9dc6e04a4 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java @@ -53,7 +53,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.RealmAuth; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; /** * @author Pedro Igor @@ -61,12 +61,12 @@ import org.keycloak.services.resources.admin.RealmAuth; public class ResourceServerService { private final AuthorizationProvider authorization; - private final RealmAuth auth; + private final AdminPermissionEvaluator auth; private final KeycloakSession session; private ResourceServer resourceServer; private final ClientModel client; - public ResourceServerService(AuthorizationProvider authorization, ResourceServer resourceServer, ClientModel client, RealmAuth auth) { + public ResourceServerService(AuthorizationProvider authorization, ResourceServer resourceServer, ClientModel client, AdminPermissionEvaluator auth) { this.authorization = authorization; this.session = authorization.getKeycloakSession(); this.client = client; @@ -75,7 +75,7 @@ public class ResourceServerService { } public void create() { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); UserModel serviceAccount = this.session.users().getServiceAccount(client); @@ -92,7 +92,7 @@ public class ResourceServerService { @Consumes("application/json") @Produces("application/json") public Response update(ResourceServerRepresentation server) { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); this.resourceServer.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement()); this.resourceServer.setPolicyEnforcementMode(server.getPolicyEnforcementMode()); @@ -100,7 +100,7 @@ public class ResourceServerService { } public void delete() { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); StoreFactory storeFactory = authorization.getStoreFactory(); ResourceStore resourceStore = storeFactory.getResourceStore(); String id = resourceServer.getId(); @@ -121,7 +121,7 @@ public class ResourceServerService { @GET @Produces("application/json") public Response findById() { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); return Response.ok(toRepresentation(this.resourceServer, this.client)).build(); } @@ -129,7 +129,7 @@ public class ResourceServerService { @GET @Produces("application/json") public Response exportSettings() { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); return Response.ok(ExportUtils.exportAuthorizationSettings(session, client)).build(); } @@ -137,7 +137,7 @@ public class ResourceServerService { @POST @Consumes(MediaType.APPLICATION_JSON) public Response importSettings(@Context final UriInfo uriInfo, ResourceServerRepresentation rep) throws IOException { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); rep.setClientId(client.getId()); @@ -175,7 +175,7 @@ public class ResourceServerService { @Path("/permission") public Object getPermissionTypeResource() { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); PermissionService resource = new PermissionService(this.resourceServer, this.authorization, this.auth); ResteasyProviderFactory.getInstance().injectProperties(resource); diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java index 5d6592366d..c8eaafe04c 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java @@ -37,7 +37,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.RealmAuth; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -69,10 +69,10 @@ import static org.keycloak.models.utils.RepresentationToModel.toModel; public class ResourceSetService { private final AuthorizationProvider authorization; - private final RealmAuth auth; + private final AdminPermissionEvaluator auth; private ResourceServer resourceServer; - public ResourceSetService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { + public ResourceSetService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth) { this.resourceServer = resourceServer; this.authorization = authorization; this.auth = auth; @@ -272,7 +272,7 @@ public class ResourceSetService { @Produces("application/json") @NoCache public Response find(@QueryParam("name") String name) { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); StoreFactory storeFactory = authorization.getStoreFactory(); if (name == null) { @@ -367,13 +367,13 @@ public class ResourceSetService { private void requireView() { if (this.auth != null) { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); } } private void requireManage() { if (this.auth != null) { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); } } } \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java index 1ec9a139e0..157b3a0f37 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java @@ -30,7 +30,7 @@ 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.RealmAuth; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -60,10 +60,10 @@ import static org.keycloak.models.utils.RepresentationToModel.toModel; public class ScopeService { private final AuthorizationProvider authorization; - private final RealmAuth auth; + private final AdminPermissionEvaluator auth; private ResourceServer resourceServer; - public ScopeService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { + public ScopeService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth) { this.resourceServer = resourceServer; this.authorization = authorization; this.auth = auth; @@ -73,7 +73,7 @@ public class ScopeService { @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response create(ScopeRepresentation scope) { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); Scope model = toModel(scope, this.resourceServer, authorization); scope.setId(model.getId()); @@ -86,7 +86,7 @@ public class ScopeService { @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response update(@PathParam("id") String id, ScopeRepresentation scope) { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); scope.setId(id); StoreFactory storeFactory = authorization.getStoreFactory(); Scope model = storeFactory.getScopeStore().findById(scope.getId(), resourceServer.getId()); @@ -103,7 +103,7 @@ public class ScopeService { @Path("{id}") @DELETE public Response delete(@PathParam("id") String id) { - this.auth.requireManage(); + this.auth.realm().requireManageAuthorization(); StoreFactory storeFactory = authorization.getStoreFactory(); List resources = storeFactory.getResourceStore().findByScope(Arrays.asList(id), resourceServer.getId()); @@ -137,7 +137,7 @@ public class ScopeService { @GET @Produces(MediaType.APPLICATION_JSON) public Response findById(@PathParam("id") String id) { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); Scope model = this.authorization.getStoreFactory().getScopeStore().findById(id, resourceServer.getId()); if (model == null) { @@ -151,7 +151,7 @@ public class ScopeService { @GET @Produces(MediaType.APPLICATION_JSON) public Response getResources(@PathParam("id") String id) { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); StoreFactory storeFactory = this.authorization.getStoreFactory(); Scope model = storeFactory.getScopeStore().findById(id, resourceServer.getId()); @@ -173,7 +173,7 @@ public class ScopeService { @GET @Produces(MediaType.APPLICATION_JSON) public Response getPermissions(@PathParam("id") String id) { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); StoreFactory storeFactory = this.authorization.getStoreFactory(); Scope model = storeFactory.getScopeStore().findById(id, resourceServer.getId()); @@ -199,7 +199,7 @@ public class ScopeService { @Produces(MediaType.APPLICATION_JSON) @NoCache public Response find(@QueryParam("name") String name) { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); StoreFactory storeFactory = authorization.getStoreFactory(); if (name == null) { @@ -222,7 +222,7 @@ public class ScopeService { @QueryParam("deep") Boolean deep, @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResult) { - this.auth.requireView(); + this.auth.realm().requireViewAuthorization(); if (deep == null) { deep = true; diff --git a/services/src/main/java/org/keycloak/authorization/admin/permissions/MgmtPermissions.java b/services/src/main/java/org/keycloak/authorization/admin/permissions/MgmtPermissions.java deleted file mode 100644 index 78e41e373e..0000000000 --- a/services/src/main/java/org/keycloak/authorization/admin/permissions/MgmtPermissions.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.keycloak.authorization.admin.permissions; - -import org.keycloak.Config; -import org.keycloak.authorization.AuthorizationProvider; -import org.keycloak.authorization.AuthorizationProviderFactory; -import org.keycloak.authorization.common.KeycloakIdentity; -import org.keycloak.authorization.common.UserModelIdentity; -import org.keycloak.authorization.identity.Identity; -import org.keycloak.authorization.model.ResourceServer; -import org.keycloak.models.ClientModel; -import org.keycloak.models.Constants; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; -import org.keycloak.representations.AccessToken; -import org.keycloak.services.managers.RealmManager; -import org.keycloak.services.resources.admin.AdminAuth; -import org.keycloak.services.resources.admin.RealmAuth; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class MgmtPermissions { - public static final String MANAGE_SCOPE = "manage"; - - protected RealmModel realm; - protected KeycloakSession session; - protected AuthorizationProvider authz; - protected AdminAuth auth; - protected Identity identity; - - public MgmtPermissions(KeycloakSession session, RealmModel realm) { - this.session = session; - this.realm = realm; - KeycloakSessionFactory keycloakSessionFactory = session.getKeycloakSessionFactory(); - AuthorizationProviderFactory factory = (AuthorizationProviderFactory) keycloakSessionFactory.getProviderFactory(AuthorizationProvider.class); - this.authz = factory.create(session, realm); - } - public MgmtPermissions(KeycloakSession session, RealmModel realm, AdminAuth auth) { - this(session, realm); - this.auth = auth; - - } - - public ClientModel getRealmManagementClient() { - ClientModel client = null; - if (realm.getName().equals(Config.getAdminRealm())) { - client = realm.getClientByClientId(Config.getAdminRealm() + "-realm"); - } else { - client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); - - } - return client; - } - - public boolean isAdminSameRealm() { - return auth == null || realm.getId().equals(auth.getRealm().getId()); - } - - public RealmAuth getRealmAuth() { - if (auth == null) return null; - RealmManager realmManager = new RealmManager(session); - if (auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) { - return new RealmAuth(auth, realm.getMasterAdminClient()); - } else { - return new RealmAuth(auth, realm.getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm()))); - } - } - - public Identity identity() { - if (identity != null) return identity; - if (auth.getClient().getClientId().equals(Constants.ADMIN_CLI_CLIENT_ID)) { - this.identity = new UserModelIdentity(realm, auth.getUser()); - - } else { - this.identity = new KeycloakIdentity(auth.getToken(), session); - } - return this.identity; - } - - public void setIdentity(UserModel user) { - this.identity = new UserModelIdentity(realm, user); - } - - - public RoleMgmtPermissions roles() { - return new RoleMgmtPermissions(session, realm, authz, this); - } - - public UsersPermissions users() { return new UsersPermissions(session, realm, authz, this); } - - public ResourceServer findOrCreateResourceServer(ClientModel client) { - ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); - if (server == null) { - server = authz.getStoreFactory().getResourceServerStore().create(client.getId()); - } - return server; - } - -} diff --git a/services/src/main/java/org/keycloak/authorization/admin/permissions/RoleMgmtPermissions.java b/services/src/main/java/org/keycloak/authorization/admin/permissions/RoleMgmtPermissions.java deleted file mode 100644 index 271ef0630a..0000000000 --- a/services/src/main/java/org/keycloak/authorization/admin/permissions/RoleMgmtPermissions.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.keycloak.authorization.admin.permissions; - -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.authorization.AuthorizationProvider; -import org.keycloak.authorization.Decision; -import org.keycloak.authorization.common.DefaultEvaluationContext; -import org.keycloak.authorization.identity.Identity; -import org.keycloak.authorization.model.Policy; -import org.keycloak.authorization.model.Resource; -import org.keycloak.authorization.model.ResourceServer; -import org.keycloak.authorization.model.Scope; -import org.keycloak.authorization.permission.ResourcePermission; -import org.keycloak.authorization.permission.evaluator.PermissionEvaluator; -import org.keycloak.authorization.policy.evaluation.DecisionResult; -import org.keycloak.authorization.policy.evaluation.EvaluationContext; -import org.keycloak.authorization.store.ResourceStore; -import org.keycloak.authorization.util.Permissions; -import org.keycloak.models.AdminRoles; -import org.keycloak.models.ClientModel; -import org.keycloak.models.Constants; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; - -import java.util.List; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class RoleMgmtPermissions { - private static final Logger logger = Logger.getLogger(RoleMgmtPermissions.class); - public static final String MAP_ROLE_SCOPE = "map-role"; - protected final KeycloakSession session; - protected final RealmModel realm; - protected final AuthorizationProvider authz; - protected final MgmtPermissions root; - - public RoleMgmtPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) { - this.session = session; - this.realm = realm; - this.authz = authz; - this.root = root; - } - - public boolean isPermissionsEnabled(RoleModel role) { - return mapRolePermission(role) != null; - } - - public void setPermissionsEnabled(RoleModel role, boolean enable) { - if (enable) { - ResourceServer server = getResourceServer(role); - if (authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()) != null) { - return; - } - createResource(role); - } else { - ResourceServer server = resourceServer(role); - if (server == null) return; - Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()); - if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId()); - Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRoleScopePermissionName(role), server.getId()); - if (policy != null) authz.getStoreFactory().getPolicyStore().delete(policy.getId()); - } - } - - public Policy mapRolePermission(RoleModel role) { - ResourceServer server = resourceServer(role); - if (server == null) return null; - - Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()); - if (resource == null) return null; - - return authz.getStoreFactory().getPolicyStore().findByName(getMapRoleScopePermissionName(role), server.getId()); - } - - public Resource resource(RoleModel role) { - ResourceStore resourceStore = authz.getStoreFactory().getResourceStore(); - ResourceServer server = resourceServer(role); - if (server == null) return null; - return resourceStore.findByName(getRoleResourceName(role), server.getId()); - } - - public ResourceServer resourceServer(RoleModel role) { - ClientModel client = getRoleClient(role); - return authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); - } - - /** - * Is admin allowed to map this role? In Authz terms, does the user have the "map-role" scope for the role's Authz resource? - * - * This method is hardcoded to return TRUE if any of these conditions are met: - * - The admin is from the master realm managing a different realm - * - If the Authz objects are not set up correctly for this role (resource server, resource, permission) - * - If the role's mapRole permission does not have a policy associated with it. - * - * Otherwise, it will use the Authz policy engine to resolve this answer. - * - * @param role - * @return - */ - public boolean canMapRole(RoleModel role) { - if (!root.isAdminSameRealm()) { - return true; - } - if (!isPermissionsEnabled(role)) return true; // no authz permissions set up so just allow it. - - ResourceServer resourceServer = getResourceServer(role); - Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRoleScopePermissionName(role), resourceServer.getId()); - if (policy.getAssociatedPolicies().isEmpty()) { - return true; // if no policies applied, just ignore - } - - RealmModel oldRealm = session.getContext().getRealm(); - try { - session.getContext().setRealm(realm); - Identity identity = root.identity(); - - EvaluationContext context = new DefaultEvaluationContext(identity, session); - DecisionResult decisionCollector = new DecisionResult(); - Resource roleResource = resource(role); - Scope mapRoleScope = getMapRoleScope(resourceServer); - - List permissions = Permissions.permission(resourceServer, roleResource, mapRoleScope); - PermissionEvaluator from = authz.evaluators().from(permissions, context); - from.evaluate(decisionCollector); - if (!decisionCollector.completed()) { - logger.error("Failed to run map role policy check", decisionCollector.getError()); - return false; - } - return decisionCollector.getResults().get(0).getEffect() == Decision.Effect.PERMIT; - } finally { - session.getContext().setRealm(oldRealm); - } - } - - private ClientModel getRoleClient(RoleModel role) { - ClientModel client = null; - if (role.getContainer() instanceof ClientModel) { - client = (ClientModel)role.getContainer(); - } else { - client = root.getRealmManagementClient(); - } - return client; - } - - public Policy manageUsersPolicy(ResourceServer server) { - RoleModel role = root.getRealmManagementClient().getRole(AdminRoles.MANAGE_USERS); - return rolePolicy(server, role); - } - - public Policy rolePolicy(ResourceServer server, RoleModel role) { - String policyName = Helper.getRolePolicyName(role); - Policy policy = authz.getStoreFactory().getPolicyStore().findByName(policyName, server.getId()); - if (policy != null) return policy; - return Helper.createRolePolicy(authz, server, role, policyName); - } - - private Scope getMapRoleScope(ResourceServer server) { - Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_SCOPE, server.getId()); - if (scope == null) { - scope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_SCOPE, server); - } - return scope; - } - - - private Resource createResource(RoleModel role) { - ResourceServer server = getResourceServer(role); - Resource resource = authz.getStoreFactory().getResourceStore().create(getRoleResourceName(role), server, server.getClientId()); - resource.setType("Role"); - Scope mapRoleScope = getMapRoleScope(server); - Helper.addEmptyScopePermission(authz, server, getMapRoleScopePermissionName(role), resource, mapRoleScope); - return resource; - } - - private String getMapRoleScopePermissionName(RoleModel role) { - return MAP_ROLE_SCOPE + ".permission." + role.getName(); - } - - private ResourceServer getResourceServer(RoleModel role) { - ClientModel client = getRoleClient(role); - return root.findOrCreateResourceServer(client); - } - - private static String getRoleResourceName(RoleModel role) { - return "role.resource." + role.getName(); - } - - -} diff --git a/services/src/main/java/org/keycloak/authorization/admin/permissions/UsersPermissions.java b/services/src/main/java/org/keycloak/authorization/admin/permissions/UsersPermissions.java deleted file mode 100644 index c5698d97c7..0000000000 --- a/services/src/main/java/org/keycloak/authorization/admin/permissions/UsersPermissions.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.keycloak.authorization.admin.permissions; - -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.authorization.AuthorizationProvider; -import org.keycloak.authorization.Decision; -import org.keycloak.authorization.common.DefaultEvaluationContext; -import org.keycloak.authorization.identity.Identity; -import org.keycloak.authorization.model.Policy; -import org.keycloak.authorization.model.Resource; -import org.keycloak.authorization.model.ResourceServer; -import org.keycloak.authorization.model.Scope; -import org.keycloak.authorization.permission.ResourcePermission; -import org.keycloak.authorization.permission.evaluator.PermissionEvaluator; -import org.keycloak.authorization.policy.evaluation.DecisionResult; -import org.keycloak.authorization.policy.evaluation.EvaluationContext; -import org.keycloak.authorization.store.ResourceServerStore; -import org.keycloak.authorization.util.Permissions; -import org.keycloak.models.AdminRoles; -import org.keycloak.models.ClientModel; -import org.keycloak.models.Constants; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserModel; -import org.keycloak.services.resources.admin.RealmAuth; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Manages default policies for all users. - * - * - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class UsersPermissions { - private static final Logger logger = Logger.getLogger(UsersPermissions.class); - public static final String MANAGE_PERMISSION_USERS = "manage.permission.users"; - public static final String USERS_RESOURCE = "Users"; - protected final KeycloakSession session; - protected final RealmModel realm; - protected final AuthorizationProvider authz; - protected final MgmtPermissions root; - - public UsersPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) { - this.session = session; - this.realm = realm; - this.authz = authz; - this.root = root; - } - - - private void initialize() { - ClientModel client = root.getRealmManagementClient(); - ResourceServer server = root.findOrCreateResourceServer(client); - Scope manageScope = authz.getStoreFactory().getScopeStore().findByName(MgmtPermissions.MANAGE_SCOPE, server.getId()); - if (manageScope == null) { - manageScope = authz.getStoreFactory().getScopeStore().create(MgmtPermissions.MANAGE_SCOPE, server); - - } - Resource usersResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); - if (usersResource == null) { - usersResource = authz.getStoreFactory().getResourceStore().create(USERS_RESOURCE, server, server.getClientId()); - } - Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId()); - if (policy == null) { - Set scopeset = new HashSet<>(); - scopeset.add(manageScope); - usersResource.updateScopes(scopeset); - Policy manageUsersPolicy = root.roles().manageUsersPolicy(server); - Helper.addScopePermission(authz, server, MANAGE_PERMISSION_USERS, usersResource, manageScope, manageUsersPolicy); - } - } - - public boolean isPermissionsEnabled() { - ResourceServer server = resourceServer(); - if (server == null) return false; - - Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); - if (resource == null) return false; - - Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId()); - - return policy != null; - } - - public void setPermissionsEnabled(boolean enable) { - ClientModel client = root.getRealmManagementClient(); - if (enable) { - initialize(); - } else { - ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); - if (server == null) return; - Resource usersResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); - if (usersResource == null) { - authz.getStoreFactory().getResourceStore().delete(usersResource.getId()); - } - Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId()); - if (policy == null) { - authz.getStoreFactory().getPolicyStore().delete(policy.getId()); - - } - } - } - - private Resource getUsersResource(ResourceServer server) { - Resource usersResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); - if (usersResource == null) { - usersResource = authz.getStoreFactory().getResourceStore().create(USERS_RESOURCE, server, server.getClientId()); - } - return usersResource; - } - - private Scope getManageScope(ResourceServer server) { - Scope manageScope = authz.getStoreFactory().getScopeStore().findByName(MgmtPermissions.MANAGE_SCOPE, server.getId()); - if (manageScope == null) { - manageScope = authz.getStoreFactory().getScopeStore().create(MgmtPermissions.MANAGE_SCOPE, server); - - } - return manageScope; - } - - private ResourceServer getRealmManagementResourceServer() { - ClientModel client = root.getRealmManagementClient(); - return root.findOrCreateResourceServer(client); - } - - private boolean canManageDefault(UserModel admin) { - RealmAuth auth = root.getRealmAuth(); - if (auth != null) { - auth.init(RealmAuth.Resource.USER); - return auth.hasManage(); - } else { - ClientModel client = root.getRealmManagementClient(); - RoleModel manageUsers = client.getRole(AdminRoles.MANAGE_USERS); - return admin.hasRole(manageUsers); - } - - } - - public boolean canManage() { - if (root.getRealmAuth() == null) { - throw new NullPointerException("Realm auth null"); - } - return canManage(root.getRealmAuth().getAuth().getUser()); - } - - public Resource resource() { - ResourceServer server = resourceServer(); - if (server == null) return null; - - return authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); - } - - /** - * Is admin allowed to manage users? In Authz terms, does the admin have the "map-role" scope for the role's Authz resource? - * - * This method will follow the old default behavior (does the admin have the manage-users role) if any of these conditions - * are met.: - * - The admin is from the master realm managing a different realm - * - If the Authz objects are not set up correctly for the Users resource in Authz - * - The "manage" permission for the Users resource has an empty associatedPolicy list. - * - * Otherwise, it will use the Authz policy engine to resolve this answer. - * - * @param admin - * @return - */ - public boolean canManage(UserModel admin) { - if (!root.isAdminSameRealm()) { - return canManageDefault(admin); - } - - ResourceServer server = resourceServer(); - if (server == null) return canManageDefault(admin); - - Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); - if (resource == null) return canManageDefault(admin); - - Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId()); - if (policy == null) { - return canManageDefault(admin); - } - - Set associatedPolicies = policy.getAssociatedPolicies(); - // if no policies attached to permission then just do default behavior - if (associatedPolicies == null || associatedPolicies.isEmpty()) { - return canManageDefault(admin); - } - - RealmModel oldRealm = session.getContext().getRealm(); - try { - session.getContext().setRealm(realm); - Identity identity = root.identity(); - EvaluationContext context = new DefaultEvaluationContext(identity, session); - DecisionResult decisionCollector = new DecisionResult(); - ResourceServer resourceServer = getRealmManagementResourceServer(); - Resource roleResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, resourceServer.getId()); - Scope manageScope = getManageScope(resourceServer); - - List permissions = Permissions.permission(resourceServer, roleResource, manageScope); - PermissionEvaluator from = authz.evaluators().from(permissions, context); - from.evaluate(decisionCollector); - if (!decisionCollector.completed()) { - logger.error("Failed to run Users manage check", decisionCollector.getError()); - return false; - } - return decisionCollector.getResults().get(0).getEffect() == Decision.Effect.PERMIT; - } finally { - session.getContext().setRealm(oldRealm); - } - - } - - public ResourceServer resourceServer() { - ResourceServerStore resourceServerStore = authz.getStoreFactory().getResourceServerStore(); - ClientModel client = root.getRealmManagementClient(); - return authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); - } - - public Policy managePermission() { - ResourceServer server = resourceServer(); - return authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId()); - } - - -} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminAuth.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminAuth.java index 924b35e40c..98d453830f 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminAuth.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminAuth.java @@ -98,4 +98,7 @@ public class AdminAuth { return false; } + public enum Resource { + CLIENT, USER, REALM, EVENTS, IDENTITY_PROVIDER, IMPERSONATION, AUTHORIZATION + } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java index 5ac7593e1c..2154ad6380 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java @@ -26,6 +26,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserLoginFailureModel; import org.keycloak.models.UserModel; import org.keycloak.services.managers.BruteForceProtector; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.DELETE; import javax.ws.rs.GET; @@ -48,7 +49,7 @@ import java.util.Map; */ public class AttackDetectionResource { protected static final Logger logger = Logger.getLogger(AttackDetectionResource.class); - protected RealmAuth auth; + protected AdminPermissionEvaluator auth; protected RealmModel realm; private AdminEventBuilder adminEvent; @@ -64,12 +65,10 @@ public class AttackDetectionResource { @Context protected HttpHeaders headers; - public AttackDetectionResource(RealmAuth auth, RealmModel realm, AdminEventBuilder adminEvent) { + public AttackDetectionResource(AdminPermissionEvaluator auth, RealmModel realm, AdminEventBuilder adminEvent) { this.auth = auth; this.realm = realm; this.adminEvent = adminEvent.realm(realm).resource(ResourceType.USER_LOGIN_FAILURE); - - auth.init(RealmAuth.Resource.USER); } /** @@ -83,7 +82,8 @@ public class AttackDetectionResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public Map bruteForceUserStatus(@PathParam("userId") String userId) { - auth.requireView(); + UserModel user = session.users().getUserById(userId, realm); + auth.users().requireView(user); Map data = new HashMap<>(); data.put("disabled", false); @@ -92,7 +92,6 @@ public class AttackDetectionResource { data.put("lastIPFailure", "n/a"); if (!realm.isBruteForceProtected()) return data; - UserModel user = session.users().getUserById(userId, realm); UserLoginFailureModel model = session.sessions().getUserLoginFailure(realm, userId); if (model == null) return data; @@ -115,10 +114,10 @@ public class AttackDetectionResource { @Path("brute-force/users/{userId}") @DELETE public void clearBruteForceForUser(@PathParam("userId") String userId) { - auth.requireManage(); - UserLoginFailureModel model = session.sessions().getUserLoginFailure(realm, userId); if (model != null) { + UserModel user = session.users().getUserById(userId, realm); + auth.users().requireView(user); session.sessions().removeUserLoginFailure(realm, userId); adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success(); } @@ -133,7 +132,7 @@ public class AttackDetectionResource { @Path("brute-force/users") @DELETE public void clearAllBruteForce() { - auth.requireManage(); + auth.users().requireManage(); session.sessions().removeAllUserLoginFailures(realm); adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success(); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java index 2f7362bb94..ba7ecccc4b 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java @@ -50,6 +50,7 @@ import org.keycloak.representations.idm.AuthenticatorConfigRepresentation; import org.keycloak.representations.idm.ConfigPropertyRepresentation; import org.keycloak.representations.idm.RequiredActionProviderRepresentation; import org.keycloak.services.ErrorResponse; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.utils.CredentialHelper; import javax.ws.rs.Consumes; @@ -80,18 +81,17 @@ public class AuthenticationManagementResource { private final RealmModel realm; private final KeycloakSession session; - private RealmAuth auth; + private AdminPermissionEvaluator auth; private AdminEventBuilder adminEvent; @Context private UriInfo uriInfo; protected static final Logger logger = Logger.getLogger(AuthenticationManagementResource.class); - public AuthenticationManagementResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) { + public AuthenticationManagementResource(RealmModel realm, KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.realm = realm; this.session = session; this.auth = auth; - this.auth.init(RealmAuth.Resource.REALM); this.adminEvent = adminEvent.resource(ResourceType.AUTH_FLOW); } @@ -105,7 +105,7 @@ public class AuthenticationManagementResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List> getFormProviders() { - auth.requireView(); + auth.realm().requireViewRealm(); List factories = session.getKeycloakSessionFactory().getProviderFactories(FormAuthenticator.class); return buildProviderMetadata(factories); @@ -121,7 +121,7 @@ public class AuthenticationManagementResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List> getAuthenticatorProviders() { - auth.requireView(); + auth.realm().requireViewRealm(); List factories = session.getKeycloakSessionFactory().getProviderFactories(Authenticator.class); return buildProviderMetadata(factories); @@ -137,7 +137,7 @@ public class AuthenticationManagementResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List> getClientAuthenticatorProviders() { - auth.requireAny(); + auth.realm().requireViewRealm(); List factories = session.getKeycloakSessionFactory().getProviderFactories(ClientAuthenticator.class); return buildProviderMetadata(factories); @@ -167,7 +167,7 @@ public class AuthenticationManagementResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List> getFormActionProviders() { - auth.requireView(); + auth.realm().requireViewRealm(); List factories = session.getKeycloakSessionFactory().getProviderFactories(FormAction.class); return buildProviderMetadata(factories); @@ -184,7 +184,7 @@ public class AuthenticationManagementResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List getFlows() { - auth.requireAny(); + auth.realm().requireViewRealm(); List flows = new LinkedList<>(); for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) { @@ -207,7 +207,7 @@ public class AuthenticationManagementResource { @NoCache @Consumes(MediaType.APPLICATION_JSON) public Response createFlow(AuthenticationFlowRepresentation flow) { - auth.requireManage(); + auth.realm().requireManageRealm(); if (flow.getAlias() == null || flow.getAlias().isEmpty()) { return ErrorResponse.exists("Failed to create flow with empty alias name"); @@ -235,7 +235,7 @@ public class AuthenticationManagementResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public AuthenticationFlowRepresentation getFlow(@PathParam("id") String id) { - auth.requireView(); + auth.realm().requireViewRealm(); AuthenticationFlowModel flow = realm.getAuthenticationFlowById(id); if (flow == null) { @@ -253,7 +253,7 @@ public class AuthenticationManagementResource { @DELETE @NoCache public void deleteFlow(@PathParam("id") String id) { - auth.requireManage(); + auth.realm().requireManageRealm(); deleteFlow(id, true); } @@ -292,7 +292,7 @@ public class AuthenticationManagementResource { @NoCache @Consumes(MediaType.APPLICATION_JSON) public Response copy(@PathParam("flowAlias") String flowAlias, Map data) { - auth.requireManage(); + auth.realm().requireManageRealm(); String newName = data.get("newName"); if (realm.getFlowByAlias(newName) != null) { @@ -351,7 +351,7 @@ public class AuthenticationManagementResource { @NoCache @Consumes(MediaType.APPLICATION_JSON) public Response addExecutionFlow(@PathParam("flowAlias") String flowAlias, Map data) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias); if (parentFlow == null) { @@ -403,7 +403,7 @@ public class AuthenticationManagementResource { @NoCache @Consumes(MediaType.APPLICATION_JSON) public void addExecution(@PathParam("flowAlias") String flowAlias, Map data) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias); if (parentFlow == null) { @@ -450,7 +450,7 @@ public class AuthenticationManagementResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public Response getExecutions(@PathParam("flowAlias") String flowAlias) { - auth.requireView(); + auth.realm().requireViewRealm(); AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias); if (flow == null) { @@ -535,7 +535,7 @@ public class AuthenticationManagementResource { @NoCache @Consumes(MediaType.APPLICATION_JSON) public void updateExecutions(@PathParam("flowAlias") String flowAlias, AuthenticationExecutionInfoRepresentation rep) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias); if (flow == null) { @@ -566,7 +566,7 @@ public class AuthenticationManagementResource { @NoCache @Consumes(MediaType.APPLICATION_JSON) public Response addExecution(AuthenticationExecutionRepresentation execution) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticationExecutionModel model = RepresentationToModel.toModel(realm, execution); AuthenticationFlowModel parentFlow = getParentFlow(model); @@ -601,7 +601,7 @@ public class AuthenticationManagementResource { @POST @NoCache public void raisePriority(@PathParam("executionId") String execution) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution); if (model == null) { @@ -647,7 +647,7 @@ public class AuthenticationManagementResource { @POST @NoCache public void lowerPriority(@PathParam("executionId") String execution) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution); if (model == null) { @@ -687,7 +687,7 @@ public class AuthenticationManagementResource { @DELETE @NoCache public void removeExecution(@PathParam("executionId") String execution) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution); if (model == null) { @@ -723,7 +723,7 @@ public class AuthenticationManagementResource { @NoCache @Consumes(MediaType.APPLICATION_JSON) public Response newExecutionConfig(@PathParam("executionId") String execution, AuthenticatorConfigRepresentation json) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution); if (model == null) { @@ -753,7 +753,7 @@ public class AuthenticationManagementResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public AuthenticatorConfigRepresentation getAuthenticatorConfig(@PathParam("executionId") String execution,@PathParam("id") String id) { - auth.requireView(); + auth.realm().requireViewRealm(); AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id); if (config == null) { @@ -773,7 +773,7 @@ public class AuthenticationManagementResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List> getUnregisteredRequiredActions() { - auth.requireView(); + auth.realm().requireViewRealm(); List factories = session.getKeycloakSessionFactory().getProviderFactories(RequiredActionProvider.class); List> unregisteredList = new LinkedList<>(); @@ -807,7 +807,7 @@ public class AuthenticationManagementResource { @Consumes(MediaType.APPLICATION_JSON) @NoCache public void registerRequiredAction(Map data) { - auth.requireManage(); + auth.realm().requireManageRealm(); String providerId = data.get("providerId"); String name = data.get("name"); @@ -834,7 +834,7 @@ public class AuthenticationManagementResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getRequiredActions() { - auth.requireAny(); + auth.realm().requireViewRealm(); List list = new LinkedList<>(); for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) { @@ -863,7 +863,7 @@ public class AuthenticationManagementResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public RequiredActionProviderRepresentation getRequiredAction(@PathParam("alias") String alias) { - auth.requireView(); + auth.realm().requireViewRealm(); RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias); if (model == null) { @@ -883,7 +883,7 @@ public class AuthenticationManagementResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public void updateRequiredAction(@PathParam("alias") String alias, RequiredActionProviderRepresentation rep) { - auth.requireManage(); + auth.realm().requireManageRealm(); RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias); if (model == null) { @@ -909,7 +909,7 @@ public class AuthenticationManagementResource { @Path("required-actions/{alias}") @DELETE public void removeRequiredAction(@PathParam("alias") String alias) { - auth.requireManage(); + auth.realm().requireManageRealm(); RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias); if (model == null) { @@ -928,7 +928,7 @@ public class AuthenticationManagementResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public AuthenticatorConfigInfoRepresentation getAuthenticatorConfigDescription(@PathParam("providerId") String providerId) { - auth.requireView(); + auth.realm().requireViewRealm(); ConfigurableAuthenticatorFactory factory = CredentialHelper.getConfigurableAuthenticatorFactory(session, providerId); if (factory == null) { @@ -959,7 +959,7 @@ public class AuthenticationManagementResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public Map> getPerClientConfigDescription() { - auth.requireAny(); + auth.realm().requireViewRealm(); List factories = session.getKeycloakSessionFactory().getProviderFactories(ClientAuthenticator.class); @@ -991,7 +991,7 @@ public class AuthenticationManagementResource { @NoCache @Consumes(MediaType.APPLICATION_JSON) public Response createAuthenticatorConfig(AuthenticatorConfigRepresentation rep) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticatorConfigModel config = realm.addAuthenticatorConfig(RepresentationToModel.toModel(rep)); adminEvent.operation(OperationType.CREATE).resource(ResourceType.AUTHENTICATOR_CONFIG).resourcePath(uriInfo, config.getId()).representation(rep).success(); @@ -1007,7 +1007,7 @@ public class AuthenticationManagementResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public AuthenticatorConfigRepresentation getAuthenticatorConfig(@PathParam("id") String id) { - auth.requireView(); + auth.realm().requireViewRealm(); AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id); if (config == null) { @@ -1025,7 +1025,7 @@ public class AuthenticationManagementResource { @DELETE @NoCache public void removeAuthenticatorConfig(@PathParam("id") String id) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id); if (config == null) { @@ -1057,7 +1057,7 @@ public class AuthenticationManagementResource { @Consumes(MediaType.APPLICATION_JSON) @NoCache public void updateAuthenticatorConfig(@PathParam("id") String id, AuthenticatorConfigRepresentation rep) { - auth.requireManage(); + auth.realm().requireManageRealm(); AuthenticatorConfigModel exists = realm.getAuthenticatorConfigById(id); if (exists == null) { diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java index 1f4277b424..1de2c9dbc4 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java @@ -37,6 +37,7 @@ import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.KeyStoreConfig; import org.keycloak.representations.idm.CertificateRepresentation; import org.keycloak.services.ErrorResponseException; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.services.util.CertificateInfoHelper; import org.keycloak.util.JWKSUtils; import org.keycloak.util.JsonSerialization; @@ -73,13 +74,13 @@ public class ClientAttributeCertificateResource { public static final String JSON_WEB_KEY_SET = "JSON Web Key Set"; protected RealmModel realm; - private RealmAuth auth; + private AdminPermissionEvaluator auth; protected ClientModel client; protected KeycloakSession session; protected AdminEventBuilder adminEvent; protected String attributePrefix; - public ClientAttributeCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, String attributePrefix, AdminEventBuilder adminEvent) { + public ClientAttributeCertificateResource(RealmModel realm, AdminPermissionEvaluator auth, ClientModel client, KeycloakSession session, String attributePrefix, AdminEventBuilder adminEvent) { this.realm = realm; this.auth = auth; this.client = client; @@ -97,11 +98,7 @@ public class ClientAttributeCertificateResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public CertificateRepresentation getKeyInfo() { - auth.requireView(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireView(client); CertificateRepresentation info = CertificateInfoHelper.getCertificateFromClient(client, attributePrefix); return info; @@ -117,11 +114,7 @@ public class ClientAttributeCertificateResource { @Path("generate") @Produces(MediaType.APPLICATION_JSON) public CertificateRepresentation generate() { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); CertificateRepresentation info = KeycloakModelUtils.generateKeyPairCertificate(client.getClientId()); @@ -145,11 +138,7 @@ public class ClientAttributeCertificateResource { @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) public CertificateRepresentation uploadJks(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); try { CertificateRepresentation info = getCertFromRequest(input); @@ -175,11 +164,7 @@ public class ClientAttributeCertificateResource { @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) public CertificateRepresentation uploadJksCertificate(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); try { CertificateRepresentation info = getCertFromRequest(input); @@ -194,7 +179,7 @@ public class ClientAttributeCertificateResource { } private CertificateRepresentation getCertFromRequest(MultipartFormDataInput input) throws IOException { - auth.requireManage(); + auth.clients().requireManage(client); CertificateRepresentation info = new CertificateRepresentation(); Map> uploadForm = input.getFormDataMap(); String keystoreFormat = uploadForm.get("keystoreFormat").get(0).getBodyAsString(); @@ -279,11 +264,7 @@ public class ClientAttributeCertificateResource { @Produces(MediaType.APPLICATION_OCTET_STREAM) @Consumes(MediaType.APPLICATION_JSON) public byte[] getKeystore(final KeyStoreConfig config) { - auth.requireView(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireView(client); if (config.getFormat() != null && !config.getFormat().equals("JKS") && !config.getFormat().equals("PKCS12")) { throw new NotAcceptableException("Only support jks or pkcs12 format."); @@ -322,11 +303,7 @@ public class ClientAttributeCertificateResource { @Produces(MediaType.APPLICATION_OCTET_STREAM) @Consumes(MediaType.APPLICATION_JSON) public byte[] generateAndGetKeystore(final KeyStoreConfig config) { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); if (config.getFormat() != null && !config.getFormat().equals("JKS") && !config.getFormat().equals("PKCS12")) { throw new NotAcceptableException("Only support jks or pkcs12 format."); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java index dfd28cc1c3..65941afbaa 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java @@ -25,6 +25,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation; import org.keycloak.representations.idm.ClientInitialAccessPresentation; import org.keycloak.services.clientregistration.ClientRegistrationTokenUtils; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; @@ -48,7 +49,7 @@ import java.util.List; */ public class ClientInitialAccessResource { - private final RealmAuth auth; + private final AdminPermissionEvaluator auth; private final RealmModel realm; private final AdminEventBuilder adminEvent; @@ -58,12 +59,11 @@ public class ClientInitialAccessResource { @Context protected UriInfo uriInfo; - public ClientInitialAccessResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) { + public ClientInitialAccessResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.auth = auth; this.realm = realm; this.adminEvent = adminEvent.resource(ResourceType.CLIENT_INITIAL_ACCESS_MODEL); - auth.init(RealmAuth.Resource.CLIENT); } /** @@ -76,7 +76,7 @@ public class ClientInitialAccessResource { @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public ClientInitialAccessPresentation create(ClientInitialAccessCreatePresentation config, @Context final HttpServletResponse response) { - auth.requireManage(); + auth.clients().requireManage(); int expiration = config.getExpiration() != null ? config.getExpiration() : 0; int count = config.getCount() != null ? config.getCount() : 1; @@ -99,7 +99,7 @@ public class ClientInitialAccessResource { @GET @Produces(MediaType.APPLICATION_JSON) public List list() { - auth.requireView(); + auth.clients().requireView(); List models = session.sessions().listClientInitialAccess(realm); List reps = new LinkedList<>(); @@ -113,7 +113,7 @@ public class ClientInitialAccessResource { @DELETE @Path("{id}") public void delete(final @PathParam("id") String id) { - auth.requireManage(); + auth.clients().requireManage(); session.sessions().removeClientInitialAccessModel(realm, id); adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success(); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java index f8c57e259c..92503267c8 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java @@ -37,6 +37,7 @@ import org.keycloak.provider.ProviderFactory; import org.keycloak.representations.idm.ComponentTypeRepresentation; import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy; import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; /** * @resource Client Registration Policy @@ -44,7 +45,7 @@ import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyF */ public class ClientRegistrationPolicyResource { - private final RealmAuth auth; + private final AdminPermissionEvaluator auth; private final RealmModel realm; private final AdminEventBuilder adminEvent; @@ -54,12 +55,11 @@ public class ClientRegistrationPolicyResource { @Context protected UriInfo uriInfo; - public ClientRegistrationPolicyResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) { + public ClientRegistrationPolicyResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.auth = auth; this.realm = realm; this.adminEvent = adminEvent.resource(ResourceType.CLIENT_INITIAL_ACCESS_MODEL); - auth.init(RealmAuth.Resource.CLIENT); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java index ac5e6d1110..ba6bd03aea 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java @@ -51,6 +51,7 @@ import org.keycloak.services.managers.ClientManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.resources.KeycloakApplication; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.services.validation.ClientValidator; import org.keycloak.services.validation.PairwiseClientValidator; import org.keycloak.services.validation.ValidationMessages; @@ -89,7 +90,7 @@ import static java.lang.Boolean.TRUE; public class ClientResource { protected static final Logger logger = Logger.getLogger(ClientResource.class); protected RealmModel realm; - private RealmAuth auth; + private AdminPermissionEvaluator auth; private AdminEventBuilder adminEvent; protected ClientModel client; protected KeycloakSession session; @@ -104,19 +105,19 @@ public class ClientResource { return keycloak; } - public ClientResource(RealmModel realm, RealmAuth auth, ClientModel clientModel, KeycloakSession session, AdminEventBuilder adminEvent) { + public ClientResource(RealmModel realm, AdminPermissionEvaluator auth, ClientModel clientModel, KeycloakSession session, AdminEventBuilder adminEvent) { this.realm = realm; this.auth = auth; this.client = clientModel; this.session = session; this.adminEvent = adminEvent.resource(ResourceType.CLIENT); - - auth.init(RealmAuth.Resource.CLIENT); } @Path("protocol-mappers") public ProtocolMappersResource getProtocolMappers() { - ProtocolMappersResource mappers = new ProtocolMappersResource(realm, client, auth, adminEvent); + AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.clients().requireManage(client); + AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.clients().requireView(client); + ProtocolMappersResource mappers = new ProtocolMappersResource(realm, client, auth, adminEvent, manageCheck, viewCheck); ResteasyProviderFactory.getInstance().injectProperties(mappers); return mappers; } @@ -129,15 +130,11 @@ public class ClientResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public Response update(final ClientRepresentation rep) { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); ValidationMessages validationMessages = new ValidationMessages(); if (!ClientValidator.validate(rep, validationMessages) || !PairwiseClientValidator.validate(session, rep, validationMessages)) { - Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); + Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale()); throw new ErrorResponseException( validationMessages.getStringMessages(), validationMessages.getStringMessages(messages), @@ -187,11 +184,7 @@ public class ClientResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public ClientRepresentation getClient() { - auth.requireView(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireView(client); ClientRepresentation representation = ModelToRepresentation.toRepresentation(client); @@ -217,11 +210,7 @@ public class ClientResource { @NoCache @Path("installation/providers/{providerId}") public Response getInstallationProvider(@PathParam("providerId") String providerId) { - auth.requireView(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireView(client); ClientInstallationProvider provider = session.getProvider(ClientInstallationProvider.class, providerId); if (provider == null) throw new NotFoundException("Unknown Provider"); @@ -235,7 +224,7 @@ public class ClientResource { @DELETE @NoCache public void deleteClient() { - auth.requireManage(); + auth.clients().requireManage(client); if (client == null) { throw new NotFoundException("Could not find client"); @@ -256,11 +245,7 @@ public class ClientResource { @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public CredentialRepresentation regenerateSecret() { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); logger.debug("regenerateSecret"); UserCredentialModel cred = KeycloakModelUtils.generateSecret(client); @@ -279,11 +264,7 @@ public class ClientResource { @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public ClientRepresentation regenerateRegistrationAccessToken() { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); String token = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, realm, uriInfo, client, RegistrationAuth.AUTHENTICATED); @@ -304,11 +285,7 @@ public class ClientResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public CredentialRepresentation getClientSecret() { - auth.requireView(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireView(client); logger.debug("getClientSecret"); UserCredentialModel model = UserCredentialModel.secret(client.getSecret()); @@ -323,12 +300,16 @@ public class ClientResource { */ @Path("scope-mappings") public ScopeMappedResource getScopeMappedResource() { - return new ScopeMappedResource(realm, auth, client, session, adminEvent); + AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.clients().requireManage(client); + AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.clients().requireView(client); + return new ScopeMappedResource(realm, auth, client, session, adminEvent, manageCheck, viewCheck); } @Path("roles") public RoleContainerResource getRoleContainerResource() { - return new RoleContainerResource(session, uriInfo, realm, auth, client, adminEvent); + AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.clients().requireManage(client); + AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.clients().requireView(client); + return new RoleContainerResource(session, uriInfo, realm, auth, client, adminEvent, manageCheck, viewCheck); } /** @@ -341,11 +322,7 @@ public class ClientResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public UserRepresentation getServiceAccountUser() { - auth.requireView(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireView(client); UserModel user = session.users().getServiceAccount(client); if (user == null) { @@ -369,11 +346,7 @@ public class ClientResource { @POST @Produces(MediaType.APPLICATION_JSON) public GlobalRequestResult pushRevocation() { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).resource(ResourceType.CLIENT).success(); return new ResourceAdminManager(session).pushClientRevocationPolicy(uriInfo.getRequestUri(), realm, client); @@ -396,11 +369,7 @@ public class ClientResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public Map getApplicationSessionCount() { - auth.requireView(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireView(client); Map map = new HashMap<>(); map.put("count", session.sessions().getActiveUserSessions(client.getRealm(), client)); @@ -421,11 +390,7 @@ public class ClientResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List getUserSessions(@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) { - auth.requireView(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireView(client); firstResult = firstResult != null ? firstResult : -1; maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS; @@ -453,11 +418,7 @@ public class ClientResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public Map getOfflineSessionCount() { - auth.requireView(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireView(client); Map map = new HashMap<>(); map.put("count", session.sessions().getOfflineSessionsCount(client.getRealm(), client)); @@ -478,11 +439,7 @@ public class ClientResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List getOfflineUserSessions(@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) { - auth.requireView(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireView(client); firstResult = firstResult != null ? firstResult : -1; maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS; @@ -519,11 +476,7 @@ public class ClientResource { @POST @Consumes(MediaType.APPLICATION_JSON) public void registerNode(Map formParams) { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); String node = formParams.get("node"); if (node == null) { @@ -543,11 +496,7 @@ public class ClientResource { @DELETE @NoCache public void unregisterNode(final @PathParam("node") String node) { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); if (logger.isDebugEnabled()) logger.debug("Unregister node: " + node); @@ -571,11 +520,7 @@ public class ClientResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public GlobalRequestResult testNodesAvailable() { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + auth.clients().requireManage(client); logger.debug("Test availability of cluster nodes"); GlobalRequestResult result = new ResourceAdminManager(session).testNodesAvailability(uriInfo.getRequestUri(), realm, client); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java index b5f199689e..c619669cc4 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java @@ -19,7 +19,7 @@ package org.keycloak.services.resources.admin; import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; -import org.keycloak.authorization.admin.permissions.MgmtPermissions; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.models.ClientModel; @@ -61,30 +61,29 @@ public class ClientRoleMappingsResource { protected KeycloakSession session; protected RealmModel realm; - protected RealmAuth auth; + protected AdminPermissionEvaluator auth; protected RoleMapperModel user; protected ClientModel client; protected AdminEventBuilder adminEvent; private UriInfo uriInfo; - private RoleMapperResource.ManageResourcePermissionCheck manageResourcePermissionCheck; + protected AdminPermissionEvaluator.RequirePermissionCheck managePermission; + protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission; - public ClientRoleMappingsResource(UriInfo uriInfo, KeycloakSession session, RealmModel realm, RealmAuth auth, RoleMapperModel user, ClientModel client, AdminEventBuilder adminEvent) { + public ClientRoleMappingsResource(UriInfo uriInfo, KeycloakSession session, RealmModel realm, AdminPermissionEvaluator auth, + RoleMapperModel user, ClientModel client, AdminEventBuilder adminEvent, + AdminPermissionEvaluator.RequirePermissionCheck manageCheck, AdminPermissionEvaluator.RequirePermissionCheck viewCheck ) { this.uriInfo = uriInfo; this.session = session; this.realm = realm; this.auth = auth; this.user = user; this.client = client; + this.managePermission = manageCheck; + this.viewPermission = viewCheck; this.adminEvent = adminEvent.resource(ResourceType.CLIENT_ROLE_MAPPING); } - public void setManageCheck(RoleMapperResource.ManageResourcePermissionCheck mapperPermissions) { - this.manageResourcePermissionCheck = mapperPermissions; - } - - - /** * Get client-level role mappings for the user, and the app * @@ -94,11 +93,7 @@ public class ClientRoleMappingsResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getClientRoleMappings() { - auth.requireView(); - - if (user == null || client == null) { - throw new NotFoundException("Not found"); - } + viewPermission.require(); Set mappings = user.getClientRoleMappings(client); List mapRep = new ArrayList(); @@ -120,11 +115,8 @@ public class ClientRoleMappingsResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getCompositeClientRoleMappings() { - auth.requireView(); + viewPermission.require(); - if (user == null || client == null) { - throw new NotFoundException("Not found"); - } Set roles = client.getRoles(); List mapRep = new ArrayList(); @@ -144,11 +136,7 @@ public class ClientRoleMappingsResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getAvailableClientRoleMappings() { - auth.requireView(); - - if (user == null || client == null) { - throw new NotFoundException("Not found"); - } + viewPermission.require(); Set available = client.getRoles(); available = available.stream().filter(r -> @@ -171,17 +159,6 @@ public class ClientRoleMappingsResource { return mappings; } - private void checkManagePermission() { - if (manageResourcePermissionCheck == null) { - auth.requireManage(); - } else { - if (!manageResourcePermissionCheck.canManage()) { - throw new ForbiddenException(); - } - } - } - - /** * Add client-level roles to the user role mapping * @@ -190,11 +167,7 @@ public class ClientRoleMappingsResource { @POST @Consumes(MediaType.APPLICATION_JSON) public void addClientRoleMapping(List roles) { - checkManagePermission(); - - if (user == null || client == null) { - throw new NotFoundException("Not found"); - } + managePermission.require(); for (RoleRepresentation role : roles) { RoleModel roleModel = client.getRole(role.getName()); @@ -215,7 +188,7 @@ public class ClientRoleMappingsResource { } private boolean canMapRole(RoleModel roleModel) { - return new MgmtPermissions(session, realm, auth.getAuth()).roles().canMapRole(roleModel); + return auth.roles().canMapRole(roleModel); } /** @@ -226,11 +199,7 @@ public class ClientRoleMappingsResource { @DELETE @Consumes(MediaType.APPLICATION_JSON) public void deleteClientRoleMapping(List roles) { - checkManagePermission(); - - if (user == null || client == null) { - throw new NotFoundException("Not found"); - } + managePermission.require(); if (roles == null) { Set roleModels = user.getClientRoleMappings(client); @@ -257,7 +226,7 @@ public class ClientRoleMappingsResource { try { user.deleteRoleMapping(roleModel); } catch (ModelException me) { - Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); + Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale()); throw new ErrorResponseException(me.getMessage(), MessageFormat.format(messages.getProperty(me.getMessage(), me.getMessage()), me.getParameters()), Response.Status.BAD_REQUEST); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java index f760a41c1b..f076850bef 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java @@ -31,6 +31,7 @@ import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.services.ErrorResponse; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -54,7 +55,7 @@ import javax.ws.rs.core.UriInfo; public class ClientTemplateResource { protected static final Logger logger = Logger.getLogger(ClientTemplateResource.class); protected RealmModel realm; - private RealmAuth auth; + private AdminPermissionEvaluator auth; private AdminEventBuilder adminEvent; protected ClientTemplateModel template; protected KeycloakSession session; @@ -62,19 +63,20 @@ public class ClientTemplateResource { @Context protected UriInfo uriInfo; - public ClientTemplateResource(RealmModel realm, RealmAuth auth, ClientTemplateModel template, KeycloakSession session, AdminEventBuilder adminEvent) { + public ClientTemplateResource(RealmModel realm, AdminPermissionEvaluator auth, ClientTemplateModel template, KeycloakSession session, AdminEventBuilder adminEvent) { this.realm = realm; this.auth = auth; this.template = template; this.session = session; this.adminEvent = adminEvent.resource(ResourceType.CLIENT_TEMPLATE); - auth.init(RealmAuth.Resource.CLIENT); } @Path("protocol-mappers") public ProtocolMappersResource getProtocolMappers() { - ProtocolMappersResource mappers = new ProtocolMappersResource(realm, template, auth, adminEvent); + AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.clients().requireManage(template); + AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.clients().requireView(template); + ProtocolMappersResource mappers = new ProtocolMappersResource(realm, template, auth, adminEvent, manageCheck, viewCheck); ResteasyProviderFactory.getInstance().injectProperties(mappers); return mappers; } @@ -86,7 +88,9 @@ public class ClientTemplateResource { */ @Path("scope-mappings") public ScopeMappedResource getScopeMappedResource() { - return new ScopeMappedResource(realm, auth, template, session, adminEvent); + AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.clients().requireManage(template); + AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.clients().requireView(template); + return new ScopeMappedResource(realm, auth, template, session, adminEvent, manageCheck, viewCheck); } /** @@ -97,11 +101,7 @@ public class ClientTemplateResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public Response update(final ClientTemplateRepresentation rep) { - auth.requireManage(); - - if (template == null) { - throw new NotFoundException("Could not find client template"); - } + auth.clients().requireManageTemplates(); try { RepresentationToModel.updateClientTemplate(rep, template); @@ -125,11 +125,8 @@ public class ClientTemplateResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public ClientTemplateRepresentation getClient() { - auth.requireView(); + auth.clients().requireView(template); - if (template == null) { - throw new NotFoundException("Could not find client template"); - } return ModelToRepresentation.toRepresentation(template); } @@ -141,11 +138,7 @@ public class ClientTemplateResource { @DELETE @NoCache public Response deleteClientTemplate() { - auth.requireManage(); - - if (template == null) { - throw new NotFoundException("Could not find client template"); - } + auth.clients().requireManage(template); try { realm.removeClientTemplate(template.getId()); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java index 5e27712846..954f0c8790 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java @@ -18,6 +18,7 @@ package org.keycloak.services.resources.admin; import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; +import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; @@ -29,6 +30,7 @@ import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.services.ErrorResponse; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -53,18 +55,16 @@ import java.util.List; public class ClientTemplatesResource { protected static final Logger logger = Logger.getLogger(ClientTemplatesResource.class); protected RealmModel realm; - private RealmAuth auth; + private AdminPermissionEvaluator auth; private AdminEventBuilder adminEvent; @Context protected KeycloakSession session; - public ClientTemplatesResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) { + public ClientTemplatesResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.realm = realm; this.auth = auth; this.adminEvent = adminEvent.resource(ResourceType.CLIENT_TEMPLATE); - - auth.init(RealmAuth.Resource.CLIENT); } /** @@ -76,22 +76,14 @@ public class ClientTemplatesResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getClientTemplates() { - auth.requireView(); List rep = new ArrayList<>(); List clientModels = realm.getClientTemplates(); - boolean view = auth.hasView(); + boolean view = auth.clients().canViewTemplates(); for (ClientTemplateModel clientModel : clientModels) { - if (view) { + if (view || auth.clients().canView(clientModel)) { rep.add(ModelToRepresentation.toRepresentation(clientModel)); - } else { - ClientTemplateRepresentation client = new ClientTemplateRepresentation(); - client.setId(clientModel.getId()); - client.setName(clientModel.getName()); - client.setDescription(clientModel.getDescription()); - client.setProtocol(clientModel.getProtocol()); - rep.add(client); } } return rep; @@ -109,7 +101,7 @@ public class ClientTemplatesResource { @POST @Consumes(MediaType.APPLICATION_JSON) public Response createClientTemplate(final @Context UriInfo uriInfo, final ClientTemplateRepresentation rep) { - auth.requireManage(); + auth.clients().requireManageTemplates(); try { ClientTemplateModel clientModel = RepresentationToModel.createClientTemplate(session, realm, rep); @@ -131,6 +123,9 @@ public class ClientTemplatesResource { @Path("{id}") public ClientTemplateResource getClient(final @PathParam("id") String id) { ClientTemplateModel clientModel = realm.getClientTemplateById(id); + if (clientModel == null) { + throw new NotFoundException("Could not find client template"); + } ClientTemplateResource clientResource = new ClientTemplateResource(realm, auth, clientModel, session, adminEvent); ResteasyProviderFactory.getInstance().injectProperties(clientResource); return clientResource; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java index 3fa3c75158..e02f2251f7 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java @@ -26,6 +26,7 @@ import org.keycloak.common.Profile; 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; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.RealmModel; @@ -34,14 +35,18 @@ import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponseException; +import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.ClientManager; import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.services.validation.ClientValidator; import org.keycloak.services.validation.PairwiseClientValidator; import org.keycloak.services.validation.ValidationMessages; import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @@ -65,18 +70,17 @@ import java.util.Properties; public class ClientsResource { protected static final Logger logger = Logger.getLogger(ClientsResource.class); protected RealmModel realm; - private RealmAuth auth; + private AdminPermissionEvaluator auth; private AdminEventBuilder adminEvent; @Context protected KeycloakSession session; - public ClientsResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) { + public ClientsResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.realm = realm; this.auth = auth; this.adminEvent = adminEvent.resource(ResourceType.CLIENT); - auth.init(RealmAuth.Resource.CLIENT); } /** @@ -85,21 +89,20 @@ public class ClientsResource { * Returns a list of clients belonging to the realm * * @param clientId filter by clientId + * @param viewableOnly filter clients that cannot be viewed in full by admin */ @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getClients(@QueryParam("clientId") String clientId) { - auth.requireAny(); - + public List getClients(@QueryParam("clientId") String clientId, @QueryParam("viewableOnly") @DefaultValue("false") boolean viewableOnly) { List rep = new ArrayList<>(); if (clientId == null) { List clientModels = realm.getClients(); - - boolean view = auth.hasView(); + auth.clients().requireList(); + boolean view = auth.clients().canView(); for (ClientModel clientModel : clientModels) { - if (view) { + if (view || auth.clients().canView(clientModel)) { ClientRepresentation representation = ModelToRepresentation.toRepresentation(clientModel); if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) { @@ -111,7 +114,7 @@ public class ClientsResource { } rep.add(representation); - } else { + } else if (!viewableOnly) { ClientRepresentation client = new ClientRepresentation(); client.setId(clientModel.getId()); client.setClientId(clientModel.getClientId()); @@ -120,9 +123,20 @@ public class ClientsResource { } } } else { - ClientModel client = realm.getClientByClientId(clientId); - if (client != null) { - rep.add(ModelToRepresentation.toRepresentation(client)); + ClientModel clientModel = realm.getClientByClientId(clientId); + if (clientModel != null) { + if (auth.clients().canView(clientModel)) { + rep.add(ModelToRepresentation.toRepresentation(clientModel)); + } else if (!viewableOnly && auth.clients().canList()){ + ClientRepresentation client = new ClientRepresentation(); + client.setId(clientModel.getId()); + client.setClientId(clientModel.getClientId()); + client.setDescription(clientModel.getDescription()); + rep.add(client); + + } else { + throw new ForbiddenException(); + } } } return rep; @@ -144,11 +158,11 @@ public class ClientsResource { @POST @Consumes(MediaType.APPLICATION_JSON) public Response createClient(final @Context UriInfo uriInfo, final ClientRepresentation rep) { - auth.requireManage(); + auth.clients().requireManage(); ValidationMessages validationMessages = new ValidationMessages(); if (!ClientValidator.validate(rep, validationMessages) || !PairwiseClientValidator.validate(session, rep, validationMessages)) { - Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); + Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale()); throw new ErrorResponseException( validationMessages.getStringMessages(), validationMessages.getStringMessages(messages), @@ -190,6 +204,9 @@ public class ClientsResource { @Path("{id}") public ClientResource getClient(final @PathParam("id") String id) { ClientModel clientModel = realm.getClientById(id); + if (clientModel == null) { + throw new NotFoundException("Could not find client"); + } session.getContext().setClient(clientModel); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java index b39b773522..c532245e15 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java @@ -38,7 +38,7 @@ import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.ComponentTypeRepresentation; import org.keycloak.representations.idm.ConfigPropertyRepresentation; import org.keycloak.services.ErrorResponse; -import org.keycloak.services.ErrorResponseException; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; @@ -63,7 +63,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.stream.Collectors; /** * @resource Component @@ -75,7 +74,7 @@ public class ComponentResource { protected RealmModel realm; - private RealmAuth auth; + private AdminPermissionEvaluator auth; private AdminEventBuilder adminEvent; @@ -91,12 +90,10 @@ public class ComponentResource { @Context protected HttpHeaders headers; - public ComponentResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) { + public ComponentResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.auth = auth; this.realm = realm; this.adminEvent = adminEvent.resource(ResourceType.COMPONENT); - - auth.init(RealmAuth.Resource.REALM); } @GET @@ -105,7 +102,7 @@ public class ComponentResource { public List getComponents(@QueryParam("parent") String parent, @QueryParam("type") String type, @QueryParam("name") String name) { - auth.requireView(); + auth.realm().requireViewRealm(); List components = Collections.EMPTY_LIST; if (parent == null && type == null) { components = realm.getComponents(); @@ -135,7 +132,7 @@ public class ComponentResource { @POST @Consumes(MediaType.APPLICATION_JSON) public Response create(ComponentRepresentation rep) { - auth.requireManage(); + auth.realm().requireManageRealm(); try { ComponentModel model = RepresentationToModel.toModel(session, rep); if (model.getParentId() == null) model.setParentId(realm.getId()); @@ -156,7 +153,7 @@ public class ComponentResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public ComponentRepresentation getComponent(@PathParam("id") String id) { - auth.requireView(); + auth.realm().requireViewRealm(); ComponentModel model = realm.getComponent(id); if (model == null) { throw new NotFoundException("Could not find component"); @@ -169,7 +166,7 @@ public class ComponentResource { @Path("{id}") @Consumes(MediaType.APPLICATION_JSON) public Response updateComponent(@PathParam("id") String id, ComponentRepresentation rep) { - auth.requireManage(); + auth.realm().requireManageRealm(); try { ComponentModel model = realm.getComponent(id); if (model == null) { @@ -188,7 +185,7 @@ public class ComponentResource { @DELETE @Path("{id}") public void removeComponent(@PathParam("id") String id) { - auth.requireManage(); + auth.realm().requireManageRealm(); ComponentModel model = realm.getComponent(id); if (model == null) { throw new NotFoundException("Could not find component"); @@ -199,7 +196,7 @@ public class ComponentResource { } private Response localizedErrorResponse(ComponentValidationException cve) { - Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale(), "admin-messages", "messages"); + Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale(), "admin-messages", "messages"); Object[] localizedParameters = cve.getParameters()==null ? null : Arrays.asList(cve.getParameters()).stream().map((Object parameter) -> { @@ -228,7 +225,7 @@ public class ComponentResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getSubcomponentConfig(@PathParam("id") String parentId, @QueryParam("type") String subtype) { - auth.requireView(); + auth.realm().requireViewRealm(); ComponentModel parent = realm.getComponent(parentId); if (parent == null) { throw new NotFoundException("Could not find parent component"); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java index c9fe194f94..a529a4895e 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java @@ -49,6 +49,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import org.keycloak.services.ErrorResponse; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; /** * @resource Groups @@ -58,11 +59,11 @@ public class GroupResource { private final RealmModel realm; private final KeycloakSession session; - private final RealmAuth auth; + private final AdminPermissionEvaluator auth; private final AdminEventBuilder adminEvent; private final GroupModel group; - public GroupResource(RealmModel realm, GroupModel group, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) { + public GroupResource(RealmModel realm, GroupModel group, KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.realm = realm; this.session = session; this.auth = auth; @@ -81,11 +82,7 @@ public class GroupResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public GroupRepresentation getGroup() { - this.auth.requireView(); - - if (group == null) { - throw new NotFoundException("Could not find group by id"); - } + this.auth.groups().requireView(group); return ModelToRepresentation.toGroupHierarchy(group, true); } @@ -98,11 +95,7 @@ public class GroupResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public void updateGroup(GroupRepresentation rep) { - this.auth.requireManage(); - - if (group == null) { - throw new NotFoundException("Could not find group by id"); - } + this.auth.groups().requireManage(group); updateGroup(rep, group); adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success(); @@ -112,11 +105,7 @@ public class GroupResource { @DELETE public void deleteGroup() { - this.auth.requireManage(); - - if (group == null) { - throw new NotFoundException("Could not find group by id"); - } + this.auth.groups().requireManage(group); realm.removeGroup(group); adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success(); @@ -135,12 +124,8 @@ public class GroupResource { @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Response addChild(GroupRepresentation rep) { - this.auth.requireManage(); + this.auth.groups().requireManage(group); - if (group == null) { - throw new NotFoundException("Could not find group by id"); - } - for (GroupModel group : group.getSubGroups()) { if (group.getName().equals(rep.getName())) { return ErrorResponse.exists("Parent already contains subgroup named '" + rep.getName() + "'"); @@ -191,9 +176,9 @@ public class GroupResource { @Path("role-mappings") public RoleMapperResource getRoleMappings() { - auth.init(RealmAuth.Resource.USER); - - RoleMapperResource resource = new RoleMapperResource(realm, auth, group, adminEvent); + AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.groups().requireManage(group); + AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.groups().requireView(group); + RoleMapperResource resource = new RoleMapperResource(realm, auth, group, adminEvent, manageCheck, viewCheck); ResteasyProviderFactory.getInstance().injectProperties(resource); return resource; @@ -214,11 +199,8 @@ public class GroupResource { @Produces(MediaType.APPLICATION_JSON) public List getMembers(@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) { - auth.requireView(); + this.auth.groups().requireViewMembers(group); - if (group == null) { - throw new NotFoundException("Could not find group by id"); - } firstResult = firstResult != null ? firstResult : 0; maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java index 5be1c0d958..2a1909b383 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java @@ -40,6 +40,7 @@ import javax.ws.rs.core.UriInfo; import java.net.URI; import java.util.List; import org.keycloak.services.ErrorResponse; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; /** * @resource Groups @@ -49,15 +50,14 @@ public class GroupsResource { private final RealmModel realm; private final KeycloakSession session; - private final RealmAuth auth; + private final AdminPermissionEvaluator auth; private final AdminEventBuilder adminEvent; - public GroupsResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) { + public GroupsResource(RealmModel realm, KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.realm = realm; this.session = session; this.auth = auth; this.adminEvent = adminEvent.resource(ResourceType.GROUP); - auth.init(RealmAuth.Resource.USER); } @@ -72,7 +72,7 @@ public class GroupsResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List getGroups() { - auth.requireView(); + auth.groups().requireList(); return ModelToRepresentation.toGroupHierarchy(realm, false); } @@ -85,9 +85,10 @@ public class GroupsResource { */ @Path("{id}") public GroupResource getGroupById(@PathParam("id") String id) { - auth.requireView(); - GroupModel group = realm.getGroupById(id); + if (group == null) { + throw new NotFoundException("Could not find group by id"); + } GroupResource resource = new GroupResource(realm, group, session, this.auth, adminEvent); ResteasyProviderFactory.getInstance().injectProperties(resource); return resource; @@ -102,7 +103,7 @@ public class GroupsResource { @POST @Consumes(MediaType.APPLICATION_JSON) public Response addTopLevelGroup(GroupRepresentation rep) { - auth.requireManage(); + auth.groups().requireManage(); for (GroupModel group : realm.getGroups()) { if (group.getName().equals(rep.getName())) { diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java index aa4a054118..4c08c204b2 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java @@ -44,6 +44,7 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.services.ErrorResponse; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -72,7 +73,7 @@ public class IdentityProviderResource { protected static final Logger logger = Logger.getLogger(IdentityProviderResource.class); - private final RealmAuth auth; + private final AdminPermissionEvaluator auth; private final RealmModel realm; private final KeycloakSession session; private final IdentityProviderModel identityProviderModel; @@ -80,7 +81,7 @@ public class IdentityProviderResource { @Context private UriInfo uriInfo; - public IdentityProviderResource(RealmAuth auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel, AdminEventBuilder adminEvent) { + public IdentityProviderResource(AdminPermissionEvaluator auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel, AdminEventBuilder adminEvent) { this.realm = realm; this.session = session; this.identityProviderModel = identityProviderModel; @@ -97,7 +98,7 @@ public class IdentityProviderResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public IdentityProviderRepresentation getIdentityProvider() { - this.auth.requireView(); + this.auth.realm().requireViewIdentityProviders(); if (identityProviderModel == null) { throw new javax.ws.rs.NotFoundException(); @@ -115,7 +116,7 @@ public class IdentityProviderResource { @DELETE @NoCache public Response delete() { - this.auth.requireManage(); + this.auth.realm().requireManageIdentityProviders(); if (identityProviderModel == null) { throw new javax.ws.rs.NotFoundException(); @@ -138,7 +139,7 @@ public class IdentityProviderResource { @Consumes(MediaType.APPLICATION_JSON) @NoCache public Response update(IdentityProviderRepresentation providerRep) { - this.auth.requireManage(); + this.auth.realm().requireManageIdentityProviders(); if (identityProviderModel == null) { throw new javax.ws.rs.NotFoundException(); @@ -229,7 +230,7 @@ public class IdentityProviderResource { @Path("export") @NoCache public Response export(@Context UriInfo uriInfo, @QueryParam("format") String format) { - this.auth.requireView(); + this.auth.realm().requireViewIdentityProviders(); if (identityProviderModel == null) { throw new javax.ws.rs.NotFoundException(); @@ -250,7 +251,7 @@ public class IdentityProviderResource { @Path("mapper-types") @NoCache public Map getMapperTypes() { - this.auth.requireView(); + this.auth.realm().requireViewIdentityProviders(); if (identityProviderModel == null) { throw new javax.ws.rs.NotFoundException(); @@ -289,7 +290,7 @@ public class IdentityProviderResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getMappers() { - this.auth.requireView(); + this.auth.realm().requireViewIdentityProviders(); if (identityProviderModel == null) { throw new javax.ws.rs.NotFoundException(); @@ -312,7 +313,7 @@ public class IdentityProviderResource { @Path("mappers") @Consumes(MediaType.APPLICATION_JSON) public Response addMapper(IdentityProviderMapperRepresentation mapper) { - auth.requireManage(); + this.auth.realm().requireManageIdentityProviders(); if (identityProviderModel == null) { throw new javax.ws.rs.NotFoundException(); @@ -343,7 +344,7 @@ public class IdentityProviderResource { @Path("mappers/{id}") @Produces(MediaType.APPLICATION_JSON) public IdentityProviderMapperRepresentation getMapperById(@PathParam("id") String id) { - auth.requireView(); + this.auth.realm().requireViewIdentityProviders(); if (identityProviderModel == null) { throw new javax.ws.rs.NotFoundException(); @@ -365,7 +366,7 @@ public class IdentityProviderResource { @Path("mappers/{id}") @Consumes(MediaType.APPLICATION_JSON) public void update(@PathParam("id") String id, IdentityProviderMapperRepresentation rep) { - auth.requireManage(); + this.auth.realm().requireManageIdentityProviders(); if (identityProviderModel == null) { throw new javax.ws.rs.NotFoundException(); @@ -388,7 +389,7 @@ public class IdentityProviderResource { @NoCache @Path("mappers/{id}") public void delete(@PathParam("id") String id) { - auth.requireManage(); + this.auth.realm().requireManageIdentityProviders(); if (identityProviderModel == null) { throw new javax.ws.rs.NotFoundException(); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java index c5250ac35f..646b46364c 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java @@ -37,6 +37,7 @@ import org.keycloak.models.utils.StripSecretsUtils; import org.keycloak.provider.ProviderFactory; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.services.ErrorResponse; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; @@ -65,14 +66,13 @@ public class IdentityProvidersResource { private final RealmModel realm; private final KeycloakSession session; - private RealmAuth auth; + private AdminPermissionEvaluator auth; private AdminEventBuilder adminEvent; - public IdentityProvidersResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) { + public IdentityProvidersResource(RealmModel realm, KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.realm = realm; this.session = session; this.auth = auth; - this.auth.init(RealmAuth.Resource.IDENTITY_PROVIDER); this.adminEvent = adminEvent.resource(ResourceType.IDENTITY_PROVIDER); } @@ -87,7 +87,7 @@ public class IdentityProvidersResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public Response getIdentityProviders(@PathParam("provider_id") String providerId) { - this.auth.requireView(); + this.auth.realm().requireViewIdentityProviders(); IdentityProviderFactory providerFactory = getProviderFactorytById(providerId); if (providerFactory != null) { return Response.ok(providerFactory).build(); @@ -108,7 +108,7 @@ public class IdentityProvidersResource { @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) public Map importFrom(@Context UriInfo uriInfo, MultipartFormDataInput input) throws IOException { - this.auth.requireManage(); + this.auth.realm().requireManageIdentityProviders(); Map> formDataMap = input.getFormDataMap(); if (!(formDataMap.containsKey("providerId") && formDataMap.containsKey("file"))) { throw new BadRequestException(); @@ -134,7 +134,7 @@ public class IdentityProvidersResource { @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Map importFrom(@Context UriInfo uriInfo, Map data) throws IOException { - this.auth.requireManage(); + this.auth.realm().requireManageIdentityProviders(); if (!(data.containsKey("providerId") && data.containsKey("fromUrl"))) { throw new BadRequestException(); } @@ -164,7 +164,7 @@ public class IdentityProvidersResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List getIdentityProviders() { - this.auth.requireView(); + this.auth.realm().requireViewIdentityProviders(); List representations = new ArrayList(); @@ -185,7 +185,7 @@ public class IdentityProvidersResource { @Path("instances") @Consumes(MediaType.APPLICATION_JSON) public Response create(@Context UriInfo uriInfo, IdentityProviderRepresentation representation) { - this.auth.requireManage(); + this.auth.realm().requireManageIdentityProviders(); try { IdentityProviderModel identityProvider = RepresentationToModel.toModel(realm, representation); @@ -203,7 +203,7 @@ public class IdentityProvidersResource { @Path("instances/{alias}") public IdentityProviderResource getIdentityProvider(@PathParam("alias") String alias) { - this.auth.requireView(); + this.auth.realm().requireViewIdentityProviders(); IdentityProviderModel identityProviderModel = null; for (IdentityProviderModel storedIdentityProvider : this.realm.getIdentityProviders()) { diff --git a/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java b/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java index f2c94015cd..d990fd109d 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java @@ -26,6 +26,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeyManager; import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.KeysMetadataRepresentation; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.GET; import javax.ws.rs.Produces; @@ -43,9 +44,9 @@ public class KeyResource { private RealmModel realm; private KeycloakSession session; - private RealmAuth auth; + private AdminPermissionEvaluator auth; - public KeyResource(RealmModel realm, KeycloakSession session, RealmAuth auth) { + public KeyResource(RealmModel realm, KeycloakSession session, AdminPermissionEvaluator auth) { this.realm = realm; this.session = session; this.auth = auth; @@ -55,7 +56,7 @@ public class KeyResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public KeysMetadataRepresentation getKeyMetadata() { - auth.requireView(); + auth.realm().requireViewRealm(); KeyManager keystore = session.keys(); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java index ad473b9072..3aac3315d6 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java @@ -33,7 +33,7 @@ import org.keycloak.protocol.ProtocolMapperConfigException; import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponseException; -import org.keycloak.services.resources.admin.RealmAuth.Resource; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -66,7 +66,9 @@ public class ProtocolMappersResource { protected ProtocolMapperContainerModel client; - protected RealmAuth auth; + protected AdminPermissionEvaluator auth; + protected AdminPermissionEvaluator.RequirePermissionCheck managePermission; + protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission; protected AdminEventBuilder adminEvent; @@ -76,13 +78,17 @@ public class ProtocolMappersResource { @Context protected KeycloakSession session; - public ProtocolMappersResource(RealmModel realm, ProtocolMapperContainerModel client, RealmAuth auth, AdminEventBuilder adminEvent) { + public ProtocolMappersResource(RealmModel realm, ProtocolMapperContainerModel client, AdminPermissionEvaluator auth, + AdminEventBuilder adminEvent, + AdminPermissionEvaluator.RequirePermissionCheck managePermission, + AdminPermissionEvaluator.RequirePermissionCheck viewPermission) { this.realm = realm; this.auth = auth; this.client = client; this.adminEvent = adminEvent.resource(ResourceType.PROTOCOL_MAPPER); + this.managePermission = managePermission; + this.viewPermission = viewPermission; - auth.init(Resource.CLIENT); } /** @@ -96,11 +102,7 @@ public class ProtocolMappersResource { @Path("protocol/{protocol}") @Produces(MediaType.APPLICATION_JSON) public List getMappersPerProtocol(@PathParam("protocol") String protocol) { - auth.requireAny(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + viewPermission.require(); List mappers = new LinkedList(); for (ProtocolMapperModel mapper : client.getProtocolMappers()) { @@ -119,11 +121,7 @@ public class ProtocolMappersResource { @NoCache @Consumes(MediaType.APPLICATION_JSON) public Response createMapper(ProtocolMapperRepresentation rep) { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + managePermission.require(); ProtocolMapperModel model = null; try { @@ -147,11 +145,7 @@ public class ProtocolMappersResource { @NoCache @Consumes(MediaType.APPLICATION_JSON) public void createMapper(List reps) { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + managePermission.require(); ProtocolMapperModel model = null; for (ProtocolMapperRepresentation rep : reps) { @@ -172,11 +166,7 @@ public class ProtocolMappersResource { @Path("models") @Produces(MediaType.APPLICATION_JSON) public List getMappers() { - auth.requireAny(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + viewPermission.require(); List mappers = new LinkedList(); for (ProtocolMapperModel mapper : client.getProtocolMappers()) { @@ -196,11 +186,7 @@ public class ProtocolMappersResource { @Path("models/{id}") @Produces(MediaType.APPLICATION_JSON) public ProtocolMapperRepresentation getMapperById(@PathParam("id") String id) { - auth.requireAny(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + viewPermission.require(); ProtocolMapperModel model = client.getProtocolMapperById(id); if (model == null) throw new NotFoundException("Model not found"); @@ -218,11 +204,7 @@ public class ProtocolMappersResource { @Path("models/{id}") @Consumes(MediaType.APPLICATION_JSON) public void update(@PathParam("id") String id, ProtocolMapperRepresentation rep) { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + managePermission.require(); ProtocolMapperModel model = client.getProtocolMapperById(id); if (model == null) throw new NotFoundException("Model not found"); @@ -243,11 +225,7 @@ public class ProtocolMappersResource { @NoCache @Path("models/{id}") public void delete(@PathParam("id") String id) { - auth.requireManage(); - - if (client == null) { - throw new NotFoundException("Could not find client"); - } + managePermission.require(); ProtocolMapperModel model = client.getProtocolMapperById(id); if (model == null) throw new NotFoundException("Model not found"); @@ -264,7 +242,7 @@ public class ProtocolMappersResource { } } catch (ProtocolMapperConfigException ex) { logger.error(ex.getMessage()); - Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); + Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale()); throw new ErrorResponseException(ex.getMessage(), MessageFormat.format(messages.getProperty(ex.getMessageKey(), ex.getMessage()), ex.getParameters()), Response.Status.BAD_REQUEST); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java index 510a605499..0a84001b99 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -23,9 +23,9 @@ import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.Config; import org.keycloak.KeyPairVerifier; -import org.keycloak.authorization.admin.permissions.MgmtPermissions; -import org.keycloak.authorization.admin.permissions.RoleMgmtPermissions; -import org.keycloak.authorization.admin.permissions.UsersPermissions; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement; +import org.keycloak.services.resources.admin.permissions.AdminPermissions; import org.keycloak.common.ClientConnection; import org.keycloak.common.VerificationException; import org.keycloak.common.util.PemUtils; @@ -48,7 +48,6 @@ import org.keycloak.models.LDAPConstants; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelException; import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.cache.CacheRealmProvider; import org.keycloak.models.cache.UserCache; @@ -63,7 +62,6 @@ import org.keycloak.representations.adapters.action.GlobalRequestResult; import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ComponentRepresentation; -import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.ManagementPermissionReference; @@ -76,7 +74,6 @@ import org.keycloak.services.managers.LDAPConnectionTestManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.managers.UserStorageSyncManager; -import org.keycloak.services.resources.admin.RealmAuth.Resource; import org.keycloak.storage.UserStorageProviderModel; import javax.ws.rs.Consumes; @@ -114,7 +111,7 @@ import java.util.regex.PatternSyntaxException; */ public class RealmAdminResource { protected static final Logger logger = Logger.getLogger(RealmAdminResource.class); - protected RealmAuth auth; + protected AdminPermissionEvaluator auth; protected RealmModel realm; private TokenManager tokenManager; private AdminEventBuilder adminEvent; @@ -131,13 +128,11 @@ public class RealmAdminResource { @Context protected HttpHeaders headers; - public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager, AdminEventBuilder adminEvent) { + public RealmAdminResource(AdminPermissionEvaluator auth, RealmModel realm, TokenManager tokenManager, AdminEventBuilder adminEvent) { this.auth = auth; this.realm = realm; this.tokenManager = tokenManager; this.adminEvent = adminEvent.realm(realm).resource(ResourceType.REALM); - - auth.init(RealmAuth.Resource.REALM); } /** @@ -150,7 +145,7 @@ public class RealmAdminResource { @POST @Produces(MediaType.APPLICATION_JSON) public ClientRepresentation convertClientDescription(String description) { - auth.init(Resource.CLIENT).requireManage(); + auth.clients().requireManage(); if (realm == null) { throw new NotFoundException("Realm not found."); @@ -239,7 +234,9 @@ public class RealmAdminResource { */ @Path("roles") public RoleContainerResource getRoleContainerResource() { - return new RoleContainerResource(session, uriInfo, realm, auth, realm, adminEvent); + AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.realm().requireManageRealm(); + AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.realm().requireViewRealm(); + return new RoleContainerResource(session, uriInfo, realm, auth, realm, adminEvent, manageCheck, viewCheck); } /** @@ -253,15 +250,15 @@ public class RealmAdminResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public RealmRepresentation getRealm() { - if (auth.hasView()) { + if (auth.realm().canViewRealm()) { return ModelToRepresentation.toRepresentation(realm, false); } else { - auth.requireAny(); + auth.realm().requireViewRealmNameList(); RealmRepresentation rep = new RealmRepresentation(); rep.setRealm(realm.getName()); - if (auth.init(Resource.IDENTITY_PROVIDER).hasView()) { + if (auth.realm().canViewIdentityProviders()) { RealmRepresentation r = ModelToRepresentation.toRepresentation(realm, false); rep.setIdentityProviders(r.getIdentityProviders()); rep.setIdentityProviderMappers(r.getIdentityProviderMappers()); @@ -283,7 +280,7 @@ public class RealmAdminResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public Response updateRealm(final RealmRepresentation rep) { - auth.requireManage(); + auth.realm().requireManageRealm(); logger.debug("updating realm: " + realm.getName()); @@ -347,7 +344,7 @@ public class RealmAdminResource { */ @DELETE public void deleteRealm() { - auth.requireManage(); + auth.realm().requireManageRealm(); if (!new RealmManager(session).removeRealm(realm)) { throw new NotFoundException("Realm doesn't exist"); @@ -372,9 +369,9 @@ public class RealmAdminResource { @Produces(MediaType.APPLICATION_JSON) @Path("users-management-permissions") public ManagementPermissionReference getUserMgmtPermissions() { - auth.requireView(); + auth.realm().requireViewRealm(); - MgmtPermissions permissions = new MgmtPermissions(session, realm); + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); if (permissions.users().isPermissionsEnabled()) { return toUsersMgmtRef(permissions); } else { @@ -389,9 +386,9 @@ public class RealmAdminResource { @NoCache @Path("users-management-permissions") public ManagementPermissionReference setUsersManagementPermissionsEnabled(ManagementPermissionReference ref) { - auth.requireManage(); + auth.realm().requireManageRealm(); - MgmtPermissions permissions = new MgmtPermissions(session, realm); + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); permissions.users().setPermissionsEnabled(ref.isEnabled()); if (ref.isEnabled()) { return toUsersMgmtRef(permissions); @@ -401,12 +398,11 @@ public class RealmAdminResource { } - public static ManagementPermissionReference toUsersMgmtRef(MgmtPermissions permissions) { + public static ManagementPermissionReference toUsersMgmtRef(AdminPermissionManagement permissions) { ManagementPermissionReference ref = new ManagementPermissionReference(); ref.setEnabled(true); ref.setResource(permissions.users().resource().getId()); - Map scopes = new HashMap<>(); - scopes.put(MgmtPermissions.MANAGE_SCOPE, permissions.users().managePermission().getId()); + Map scopes = permissions.users().getPermissions(); ref.setScopePermissions(scopes); return ref; } @@ -436,7 +432,7 @@ public class RealmAdminResource { */ @Path("roles-by-id") public RoleByIdResource rolesById() { - RoleByIdResource resource = new RoleByIdResource(realm, auth, adminEvent); + RoleByIdResource resource = new RoleByIdResource(realm, auth, adminEvent); ResteasyProviderFactory.getInstance().injectProperties(resource); //resourceContext.initResource(resource); return resource; @@ -449,7 +445,7 @@ public class RealmAdminResource { @Path("push-revocation") @POST public GlobalRequestResult pushRevocation() { - auth.requireManage(); + auth.realm().requireManageRealm(); GlobalRequestResult result = new ResourceAdminManager(session).pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm); adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).representation(result).success(); @@ -464,7 +460,7 @@ public class RealmAdminResource { @Path("logout-all") @POST public GlobalRequestResult logoutAll() { - auth.init(RealmAuth.Resource.USER).requireManage(); + auth.users().requireManage(); session.sessions().removeUserSessions(realm); GlobalRequestResult result = new ResourceAdminManager(session).logoutAll(uriInfo.getRequestUri(), realm); @@ -481,7 +477,7 @@ public class RealmAdminResource { @Path("sessions/{session}") @DELETE public void deleteSession(@PathParam("session") String sessionId) { - auth.init(RealmAuth.Resource.USER).requireManage(); + auth.users().requireManage(); UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId); if (userSession == null) throw new NotFoundException("Sesssion not found"); @@ -503,7 +499,7 @@ public class RealmAdminResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List> getClientSessionStats() { - auth.requireView(); + auth.realm().requireViewRealm(); List> data = new LinkedList>(); for (ClientModel client : realm.getClients()) { @@ -530,7 +526,7 @@ public class RealmAdminResource { @Path("events/config") @Produces(MediaType.APPLICATION_JSON) public RealmEventsConfigRepresentation getRealmEventsConfig() { - auth.init(RealmAuth.Resource.EVENTS).requireView(); + auth.realm().requireViewEvents(); RealmEventsConfigRepresentation config = ModelToRepresentation.toEventsConfigReprensetation(realm); if (config.getEnabledEventTypes() == null || config.getEnabledEventTypes().isEmpty()) { @@ -555,7 +551,7 @@ public class RealmAdminResource { @Path("events/config") @Consumes(MediaType.APPLICATION_JSON) public void updateRealmEventsConfig(final RealmEventsConfigRepresentation rep) { - auth.init(RealmAuth.Resource.EVENTS).requireManage(); + auth.realm().requireManageEvents(); logger.debug("updating realm events config: " + realm.getName()); new RealmManager(session).updateRealmEventsConfig(rep, realm); @@ -584,7 +580,7 @@ public class RealmAdminResource { @QueryParam("user") String user, @QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo, @QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) { - auth.init(RealmAuth.Resource.EVENTS).requireView(); + auth.realm().requireViewEvents(); EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class); @@ -677,7 +673,7 @@ public class RealmAdminResource { @QueryParam("dateTo") String dateTo, @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults, @QueryParam("resourceTypes") List resourceTypes) { - auth.init(RealmAuth.Resource.EVENTS).requireView(); + auth.realm().requireViewEvents(); EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class); AdminEventQuery query = eventStore.createAdminQuery().realm(realm.getId());; @@ -770,7 +766,7 @@ public class RealmAdminResource { @Path("events") @DELETE public void clearEvents() { - auth.init(RealmAuth.Resource.EVENTS).requireManage(); + auth.realm().requireManageEvents(); EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class); eventStore.clear(realm.getId()); @@ -783,7 +779,7 @@ public class RealmAdminResource { @Path("admin-events") @DELETE public void clearAdminEvents() { - auth.init(RealmAuth.Resource.EVENTS).requireManage(); + auth.realm().requireManageEvents(); EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class); eventStore.clearAdmin(realm.getId()); @@ -805,7 +801,7 @@ public class RealmAdminResource { @QueryParam("bindDn") String bindDn, @QueryParam("bindCredential") String bindCredential, @QueryParam("useTruststoreSpi") String useTruststoreSpi, @QueryParam("connectionTimeout") String connectionTimeout, @QueryParam("componentId") String componentId) { - auth.init(RealmAuth.Resource.REALM).requireManage(); + auth.realm().requireManageRealm(); if (componentId != null && bindCredential.equals(ComponentRepresentation.SECRET_VALUE)) { bindCredential = realm.getComponent(componentId).getConfig().getFirst(LDAPConstants.BIND_CREDENTIAL); @@ -830,7 +826,7 @@ public class RealmAdminResource { @Produces(MediaType.APPLICATION_JSON) @Path("default-groups") public List getDefaultGroups() { - auth.requireView(); + auth.realm().requireViewRealm(); List defaults = new LinkedList<>(); for (GroupModel group : realm.getDefaultGroups()) { @@ -842,7 +838,7 @@ public class RealmAdminResource { @NoCache @Path("default-groups/{groupId}") public void addDefaultGroup(@PathParam("groupId") String groupId) { - auth.requireManage(); + auth.realm().requireManageRealm(); GroupModel group = realm.getGroupById(groupId); if (group == null) { @@ -857,7 +853,7 @@ public class RealmAdminResource { @NoCache @Path("default-groups/{groupId}") public void removeDefaultGroup(@PathParam("groupId") String groupId) { - auth.requireManage(); + auth.realm().requireManageRealm(); GroupModel group = realm.getGroupById(groupId); if (group == null) { @@ -882,13 +878,12 @@ public class RealmAdminResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public GroupRepresentation getGroupByPath(@PathParam("path") String path) { - auth.requireView(); - GroupModel found = KeycloakModelUtils.findGroupByPath(realm, path); if (found == null) { throw new NotFoundException("Group path does not exist"); } + auth.groups().requireView(found); return ModelToRepresentation.toGroupHierarchy(found, true); } @@ -902,7 +897,7 @@ public class RealmAdminResource { @POST @Consumes(MediaType.APPLICATION_JSON) public Response partialImport(PartialImportRepresentation rep) { - auth.requireManage(); + auth.realm().requireManageRealm(); PartialImportManager partialImport = new PartialImportManager(rep, session, realm, adminEvent); return partialImport.saveResources(); @@ -915,7 +910,7 @@ public class RealmAdminResource { @Path("clear-realm-cache") @POST public void clearRealmCache() { - auth.requireManage(); + auth.realm().requireManageRealm(); CacheRealmProvider cache = session.getProvider(CacheRealmProvider.class); if (cache != null) { @@ -932,7 +927,7 @@ public class RealmAdminResource { @Path("clear-user-cache") @POST public void clearUserCache() { - auth.requireManage(); + auth.realm().requireManageRealm(); UserCache cache = session.getProvider(UserCache.class); if (cache != null) { @@ -949,7 +944,7 @@ public class RealmAdminResource { @Path("clear-keys-cache") @POST public void clearKeysCache() { - auth.requireManage(); + auth.realm().requireManageRealm(); PublicKeyStorageProvider cache = session.getProvider(PublicKeyStorageProvider.class); if (cache != null) { diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java index 7a948d9768..9e0a89e5b9 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java @@ -34,6 +34,8 @@ import org.keycloak.services.ErrorResponse; import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.KeycloakApplication; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import org.keycloak.services.resources.admin.permissions.AdminPermissions; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -191,13 +193,7 @@ public class RealmsAdminResource { && !auth.getRealm().equals(realm)) { throw new ForbiddenException(); } - RealmAuth realmAuth; - - if (auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) { - realmAuth = new RealmAuth(auth, realm.getMasterAdminClient()); - } else { - realmAuth = new RealmAuth(auth, realm.getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm()))); - } + AdminPermissionEvaluator realmAuth = AdminPermissions.evaluator(session, realm, auth); AdminEventBuilder adminEvent = new AdminEventBuilder(realm, auth, session, clientConnection); session.getContext().setRealm(realm); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java index 9d3e1006cc..b2ae6ad776 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java @@ -19,8 +19,10 @@ package org.keycloak.services.resources.admin; import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; -import org.keycloak.authorization.admin.permissions.MgmtPermissions; -import org.keycloak.authorization.admin.permissions.RoleMgmtPermissions; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement; +import org.keycloak.services.resources.admin.permissions.AdminPermissions; +import org.keycloak.services.resources.admin.permissions.RolePermissionManagement; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.models.ClientModel; @@ -57,7 +59,7 @@ import java.util.Set; public class RoleByIdResource extends RoleResource { protected static final Logger logger = Logger.getLogger(RoleByIdResource.class); private final RealmModel realm; - private final RealmAuth auth; + private AdminPermissionEvaluator auth; private AdminEventBuilder adminEvent; @Context @@ -66,7 +68,7 @@ public class RoleByIdResource extends RoleResource { @Context private UriInfo uriInfo; - public RoleByIdResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) { + public RoleByIdResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { super(realm); this.realm = realm; @@ -85,9 +87,9 @@ public class RoleByIdResource extends RoleResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public RoleRepresentation getRole(final @PathParam("role-id") String id) { - auth.requireAny(); RoleModel roleModel = getRoleModel(id); + auth.roles().requireView(roleModel); return getRole(roleModel); } @@ -96,17 +98,7 @@ public class RoleByIdResource extends RoleResource { if (roleModel == null) { throw new NotFoundException("Could not find role with id"); } - - RealmAuth.Resource r = null; - if (roleModel.getContainer() instanceof RealmModel) { - r = RealmAuth.Resource.REALM; - } else if (roleModel.getContainer() instanceof ClientModel) { - r = RealmAuth.Resource.CLIENT; - } else if (roleModel.getContainer() instanceof UserModel) { - r = RealmAuth.Resource.USER; - } - auth.init(r); - return roleModel; + return roleModel; } /** @@ -118,9 +110,8 @@ public class RoleByIdResource extends RoleResource { @DELETE @NoCache public void deleteRole(final @PathParam("role-id") String id) { - auth.requireManage(); - RoleModel role = getRoleModel(id); + auth.roles().requireManage(role); deleteRole(role); if (role.isClientRole()) { @@ -142,9 +133,8 @@ public class RoleByIdResource extends RoleResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public void updateRole(final @PathParam("role-id") String id, final RoleRepresentation rep) { - auth.requireManage(); - RoleModel role = getRoleModel(id); + auth.roles().requireManage(role); updateRole(rep, role); if (role.isClientRole()) { @@ -166,10 +156,9 @@ public class RoleByIdResource extends RoleResource { @POST @Consumes(MediaType.APPLICATION_JSON) public void addComposites(final @PathParam("role-id") String id, List roles) { - auth.requireManage(); - RoleModel role = getRoleModel(id); - addComposites(adminEvent, uriInfo, roles, role); + auth.roles().requireManage(role); + addComposites(auth, adminEvent, uriInfo, roles, role); } /** @@ -185,11 +174,10 @@ public class RoleByIdResource extends RoleResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public Set getRoleComposites(final @PathParam("role-id") String id) { - auth.requireAny(); if (logger.isDebugEnabled()) logger.debug("*** getRoleComposites: '" + id + "'"); RoleModel role = getRoleModel(id); - auth.requireView(); + auth.roles().requireView(role); return getRoleComposites(role); } @@ -204,9 +192,9 @@ public class RoleByIdResource extends RoleResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public Set getRealmRoleComposites(final @PathParam("role-id") String id) { - auth.requireAny(); - RoleModel role = getRoleModel(id); + auth.roles().requireView(role); + auth.roles().requireView(role); return getRealmRoleComposites(role); } @@ -223,9 +211,9 @@ public class RoleByIdResource extends RoleResource { @Produces(MediaType.APPLICATION_JSON) public Set getClientRoleComposites(final @PathParam("role-id") String id, final @PathParam("client") String client) { - auth.requireAny(); RoleModel role = getRoleModel(id); + auth.roles().requireView(role); ClientModel clientModel = realm.getClientById(client); if (clientModel == null) { throw new NotFoundException("Could not find client"); @@ -243,9 +231,8 @@ public class RoleByIdResource extends RoleResource { @DELETE @Consumes(MediaType.APPLICATION_JSON) public void deleteComposites(final @PathParam("role-id") String id, List roles) { - auth.requireManage(); - RoleModel role = getRoleModel(id); + auth.roles().requireManage(role); deleteComposites(adminEvent, uriInfo, roles, role); } @@ -261,24 +248,21 @@ public class RoleByIdResource extends RoleResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public ManagementPermissionReference getManagementPermissions(final @PathParam("role-id") String id) { - auth.requireView(); - RoleModel role = getRoleModel(id); + auth.roles().requireView(role); - MgmtPermissions permissions = new MgmtPermissions(session, realm); + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); if (!permissions.roles().isPermissionsEnabled(role)) { return new ManagementPermissionReference(); } return toMgmtRef(role, permissions); } - public static ManagementPermissionReference toMgmtRef(RoleModel role, MgmtPermissions permissions) { + public static ManagementPermissionReference toMgmtRef(RoleModel role, AdminPermissionManagement permissions) { ManagementPermissionReference ref = new ManagementPermissionReference(); ref.setEnabled(true); ref.setResource(permissions.roles().resource(role).getId()); - Map scopes = new HashMap<>(); - scopes.put(RoleMgmtPermissions.MAP_ROLE_SCOPE, permissions.roles().mapRolePermission(role).getId()); - ref.setScopePermissions(scopes); + ref.setScopePermissions(permissions.roles().getPermissions(role)); return ref; } @@ -295,11 +279,10 @@ public class RoleByIdResource extends RoleResource { @Consumes(MediaType.APPLICATION_JSON) @NoCache public ManagementPermissionReference setManagementPermissionsEnabled(final @PathParam("role-id") String id, ManagementPermissionReference ref) { - auth.requireManage(); - RoleModel role = getRoleModel(id); + auth.roles().requireManage(role); - MgmtPermissions permissions = new MgmtPermissions(session, realm); + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); permissions.roles().setPermissionsEnabled(role, ref.isEnabled()); if (ref.isEnabled()) { return toMgmtRef(role, permissions); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java index 94e4defc86..7f4d49572b 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java @@ -19,8 +19,9 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; -import org.keycloak.authorization.admin.permissions.MgmtPermissions; -import org.keycloak.authorization.admin.permissions.RoleMgmtPermissions; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement; +import org.keycloak.services.resources.admin.permissions.AdminPermissions; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.models.ClientModel; @@ -37,20 +38,20 @@ import org.keycloak.services.ErrorResponse; import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; 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.UriInfo; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; /** @@ -60,13 +61,19 @@ import java.util.Set; */ public class RoleContainerResource extends RoleResource { private final RealmModel realm; - private final RealmAuth auth; + protected AdminPermissionEvaluator auth; + protected AdminPermissionEvaluator.RequirePermissionCheck managePermission; + protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission; + protected RoleContainerModel roleContainer; private AdminEventBuilder adminEvent; private UriInfo uriInfo; private KeycloakSession session; - public RoleContainerResource(KeycloakSession session, UriInfo uriInfo, RealmModel realm, RealmAuth auth, RoleContainerModel roleContainer, AdminEventBuilder adminEvent) { + public RoleContainerResource(KeycloakSession session, UriInfo uriInfo, RealmModel realm, + AdminPermissionEvaluator auth, RoleContainerModel roleContainer, AdminEventBuilder adminEvent, + AdminPermissionEvaluator.RequirePermissionCheck managePermission, + AdminPermissionEvaluator.RequirePermissionCheck viewPermission) { super(realm); this.uriInfo = uriInfo; this.realm = realm; @@ -74,6 +81,8 @@ public class RoleContainerResource extends RoleResource { this.roleContainer = roleContainer; this.adminEvent = adminEvent; this.session = session; + this.managePermission = managePermission; + this.viewPermission = viewPermission; } /** @@ -85,11 +94,7 @@ public class RoleContainerResource extends RoleResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List getRoles() { - auth.requireAny(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } + auth.roles().requireList(roleContainer); Set roleModels = roleContainer.getRoles(); List roles = new ArrayList(); @@ -108,11 +113,7 @@ public class RoleContainerResource extends RoleResource { @POST @Consumes(MediaType.APPLICATION_JSON) public Response createRole(final RoleRepresentation rep) { - auth.requireManage(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } + managePermission.require(); if (rep.getName() == null) { throw new BadRequestException(); @@ -151,16 +152,12 @@ public class RoleContainerResource extends RoleResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public RoleRepresentation getRole(final @PathParam("role-name") String roleName) { - auth.requireView(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } RoleModel roleModel = roleContainer.getRole(roleName); if (roleModel == null) { throw new NotFoundException("Could not find role"); } + auth.roles().requireView(roleModel); return getRole(roleModel); } @@ -174,16 +171,11 @@ public class RoleContainerResource extends RoleResource { @DELETE @NoCache public void deleteRole(final @PathParam("role-name") String roleName) { - auth.requireManage(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } - RoleModel role = roleContainer.getRole(roleName); if (role == null) { throw new NotFoundException("Could not find role"); } + auth.roles().requireManage(role); deleteRole(role); if (role.isClientRole()) { @@ -207,16 +199,11 @@ public class RoleContainerResource extends RoleResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public Response updateRole(final @PathParam("role-name") String roleName, final RoleRepresentation rep) { - auth.requireManage(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } - RoleModel role = roleContainer.getRole(roleName); if (role == null) { throw new NotFoundException("Could not find role"); } + auth.roles().requireManage(role); try { updateRole(rep, role); @@ -244,17 +231,12 @@ public class RoleContainerResource extends RoleResource { @POST @Consumes(MediaType.APPLICATION_JSON) public void addComposites(final @PathParam("role-name") String roleName, List roles) { - auth.requireManage(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } - - RoleModel role = roleContainer.getRole(roleName); + RoleModel role = roleContainer.getRole(roleName); if (role == null) { throw new NotFoundException("Could not find role"); } - addComposites(adminEvent, uriInfo, roles, role); + auth.roles().requireManage(role); + addComposites(auth, adminEvent, uriInfo, roles, role); } /** @@ -268,16 +250,11 @@ public class RoleContainerResource extends RoleResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public Set getRoleComposites(final @PathParam("role-name") String roleName) { - auth.requireView(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } - RoleModel role = roleContainer.getRole(roleName); if (role == null) { throw new NotFoundException("Could not find role"); } + auth.roles().requireView(role); return getRoleComposites(role); } @@ -292,16 +269,11 @@ public class RoleContainerResource extends RoleResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public Set getRealmRoleComposites(final @PathParam("role-name") String roleName) { - auth.requireView(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } - RoleModel role = roleContainer.getRole(roleName); if (role == null) { throw new NotFoundException("Could not find role"); } + auth.roles().requireView(role); return getRealmRoleComposites(role); } @@ -319,16 +291,11 @@ public class RoleContainerResource extends RoleResource { public Set getClientRoleComposites(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName, final @PathParam("client") String client) { - auth.requireView(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } - RoleModel role = roleContainer.getRole(roleName); if (role == null) { throw new NotFoundException("Could not find role"); } + auth.roles().requireView(role); ClientModel clientModel = realm.getClientById(client); if (client == null) { throw new NotFoundException("Could not find client"); @@ -350,16 +317,12 @@ public class RoleContainerResource extends RoleResource { public void deleteComposites( final @PathParam("role-name") String roleName, List roles) { - auth.requireManage(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } RoleModel role = roleContainer.getRole(roleName); if (role == null) { throw new NotFoundException("Could not find role"); } + auth.roles().requireManage(role); deleteComposites(adminEvent, uriInfo, roles, role); } @@ -375,18 +338,13 @@ public class RoleContainerResource extends RoleResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public ManagementPermissionReference getManagementPermissions(final @PathParam("role-name") String roleName) { - auth.requireView(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } - RoleModel role = roleContainer.getRole(roleName); if (role == null) { throw new NotFoundException("Could not find role"); } + auth.roles().requireView(role); - MgmtPermissions permissions = new MgmtPermissions(session, realm); + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); if (!permissions.roles().isPermissionsEnabled(role)) { return new ManagementPermissionReference(); } @@ -406,19 +364,14 @@ public class RoleContainerResource extends RoleResource { @Consumes(MediaType.APPLICATION_JSON) @NoCache public ManagementPermissionReference setManagementPermissionsEnabled(final @PathParam("role-name") String roleName, ManagementPermissionReference ref) { - auth.requireManage(); - - if (roleContainer == null) { - throw new NotFoundException("Could not find client"); - } - RoleModel role = roleContainer.getRole(roleName); if (role == null) { throw new NotFoundException("Could not find role"); } + auth.roles().requireManage(role); if (ref.isEnabled()) { - MgmtPermissions permissions = new MgmtPermissions(session, realm); + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); permissions.roles().setPermissionsEnabled(role, ref.isEnabled()); return RoleByIdResource.toMgmtRef(role, permissions); } else { diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java index 93d4cb6223..8db05913c0 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java @@ -19,7 +19,7 @@ package org.keycloak.services.resources.admin; import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; -import org.keycloak.authorization.admin.permissions.MgmtPermissions; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.common.ClientConnection; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; @@ -52,7 +52,6 @@ import javax.ws.rs.core.UriInfo; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -69,27 +68,17 @@ import java.util.stream.Collectors; */ public class RoleMapperResource { - /** - * RoleMapperResource is reused bewteen GroupResource and UserResource to manage role mappings. - * We don't know what type of resource we're managing here (user or group), so we don't know how to query the policy engine to determine - * if an action is allowed. - * - */ - public interface ManageResourcePermissionCheck { - boolean canManage(); - } - protected static final Logger logger = Logger.getLogger(RoleMapperResource.class); protected RealmModel realm; - private RealmAuth auth; - private RoleMapperModel roleMapper; private AdminEventBuilder adminEvent; - private ManageResourcePermissionCheck manageResourcePermissionCheck; + protected AdminPermissionEvaluator.RequirePermissionCheck managePermission; + protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission; + private AdminPermissionEvaluator auth; @Context protected ClientConnection clientConnection; @@ -103,18 +92,21 @@ public class RoleMapperResource { @Context protected HttpHeaders headers; - public RoleMapperResource(RealmModel realm, RealmAuth auth, RoleMapperModel roleMapper, AdminEventBuilder adminEvent) { + public RoleMapperResource(RealmModel realm, + AdminPermissionEvaluator auth, + RoleMapperModel roleMapper, + AdminEventBuilder adminEvent, + AdminPermissionEvaluator.RequirePermissionCheck manageCheck, + AdminPermissionEvaluator.RequirePermissionCheck viewCheck) { this.auth = auth; this.realm = realm; this.adminEvent = adminEvent.resource(ResourceType.REALM_ROLE_MAPPING); this.roleMapper = roleMapper; + this.managePermission = manageCheck; + this.viewPermission = viewCheck; } - public void setManageCheck(ManageResourcePermissionCheck mapperPermissions) { - this.manageResourcePermissionCheck = mapperPermissions; - } - /** * Get role mappings * @@ -124,11 +116,7 @@ public class RoleMapperResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public MappingsRepresentation getRoleMappings() { - auth.requireView(); - - if (roleMapper == null) { - throw new NotFoundException("User not found"); - } + viewPermission.require(); MappingsRepresentation all = new MappingsRepresentation(); Set realmMappings = roleMapper.getRealmRoleMappings(); @@ -173,11 +161,7 @@ public class RoleMapperResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getRealmRoleMappings() { - auth.requireView(); - - if (roleMapper == null) { - throw new NotFoundException("User not found"); - } + viewPermission.require(); Set realmMappings = roleMapper.getRealmRoleMappings(); List realmMappingsRep = new ArrayList(); @@ -199,11 +183,7 @@ public class RoleMapperResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getCompositeRealmRoleMappings() { - auth.requireView(); - - if (roleMapper == null) { - throw new NotFoundException("User not found"); - } + viewPermission.require(); Set roles = realm.getRoles(); List realmMappingsRep = new ArrayList(); @@ -225,11 +205,7 @@ public class RoleMapperResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getAvailableRealmRoleMappings() { - auth.requireView(); - - if (roleMapper == null) { - throw new NotFoundException("User not found"); - } + viewPermission.require(); Set available = realm.getRoles(); Set set = available.stream().filter(r -> @@ -247,11 +223,7 @@ public class RoleMapperResource { @POST @Consumes(MediaType.APPLICATION_JSON) public void addRealmRoleMappings(List roles) { - checkManagePermission(); - - if (roleMapper == null) { - throw new NotFoundException("User not found"); - } + managePermission.require(); logger.debugv("** addRealmRoleMappings: {0}", roles); @@ -267,16 +239,6 @@ public class RoleMapperResource { adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo).representation(roles).success(); } - private void checkManagePermission() { - if (manageResourcePermissionCheck == null) { - auth.requireManage(); - } else { - if (!manageResourcePermissionCheck.canManage()) { - throw new ForbiddenException(); - } - } - } - /** * Delete realm-level role mappings * @@ -286,11 +248,7 @@ public class RoleMapperResource { @DELETE @Consumes(MediaType.APPLICATION_JSON) public void deleteRealmRoleMappings(List roles) { - checkManagePermission(); - - if (roleMapper == null) { - throw new NotFoundException("User not found"); - } + managePermission.require(); logger.debug("deleteRealmRoleMappings"); if (roles == null) { @@ -313,7 +271,7 @@ public class RoleMapperResource { try { roleMapper.deleteRoleMapping(roleModel); } catch (ModelException me) { - Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); + Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale()); throw new ErrorResponseException(me.getMessage(), MessageFormat.format(messages.getProperty(me.getMessage(), me.getMessage()), me.getParameters()), Response.Status.BAD_REQUEST); } @@ -332,16 +290,18 @@ public class RoleMapperResource { } private boolean canMapRole(RoleModel roleModel) { - return new MgmtPermissions(session, realm, auth.getAuth()).roles().canMapRole(roleModel); + return auth.roles().canMapRole(roleModel); } @Path("clients/{client}") public ClientRoleMappingsResource getUserClientRoleMappingsResource(@PathParam("client") String client) { ClientModel clientModel = realm.getClientById(client); - ClientRoleMappingsResource resource = new ClientRoleMappingsResource(uriInfo, session, realm, auth, roleMapper, clientModel, adminEvent); - resource.setManageCheck(() -> { - return new MgmtPermissions(session, realm, auth.getAuth()).users().canManage(); - }); + if (clientModel == null) { + throw new NotFoundException("Client not found"); + } + ClientRoleMappingsResource resource = new ClientRoleMappingsResource(uriInfo, session, realm, auth, roleMapper, + clientModel, adminEvent, + managePermission, viewPermission); return resource; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java index 5fb1d3423b..cdd9cd7a20 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java @@ -25,6 +25,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.core.UriInfo; import java.util.Collections; @@ -60,12 +61,13 @@ public abstract class RoleResource { if (rep.isScopeParamRequired() != null) role.setScopeParamRequired(rep.isScopeParamRequired()); } - protected void addComposites(AdminEventBuilder adminEvent, UriInfo uriInfo, List roles, RoleModel role) { + protected void addComposites(AdminPermissionEvaluator auth, AdminEventBuilder adminEvent, UriInfo uriInfo, List roles, RoleModel role) { for (RoleRepresentation rep : roles) { RoleModel composite = realm.getRoleById(rep.getId()); if (composite == null) { throw new NotFoundException("Could not find composite role"); } + auth.roles().requireManage(composite); role.addCompositeRole(composite); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java index 431e97cc98..4f7b5dcd6f 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java @@ -29,6 +29,7 @@ import org.keycloak.models.ScopeContainerModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -49,19 +50,25 @@ import java.util.Set; */ public class ScopeMappedClientResource { protected RealmModel realm; - private RealmAuth auth; + protected AdminPermissionEvaluator auth; + protected AdminPermissionEvaluator.RequirePermissionCheck managePermission; + protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission; protected ScopeContainerModel scopeContainer; protected KeycloakSession session; protected ClientModel scopedClient; protected AdminEventBuilder adminEvent; - public ScopeMappedClientResource(RealmModel realm, RealmAuth auth, ScopeContainerModel scopeContainer, KeycloakSession session, ClientModel scopedClient, AdminEventBuilder adminEvent) { + public ScopeMappedClientResource(RealmModel realm, AdminPermissionEvaluator auth, ScopeContainerModel scopeContainer, KeycloakSession session, ClientModel scopedClient, AdminEventBuilder adminEvent, + AdminPermissionEvaluator.RequirePermissionCheck managePermission, + AdminPermissionEvaluator.RequirePermissionCheck viewPermission) { this.realm = realm; this.auth = auth; this.scopeContainer = scopeContainer; this.session = session; this.scopedClient = scopedClient; this.adminEvent = adminEvent.resource(ResourceType.CLIENT_SCOPE_MAPPING); + this.managePermission = managePermission; + this.viewPermission = viewPermission; } /** @@ -75,11 +82,7 @@ public class ScopeMappedClientResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getClientScopeMappings() { - auth.requireView(); - - if (scopeContainer == null) { - throw new NotFoundException("Could not find client"); - } + viewPermission.require(); Set mappings = KeycloakModelUtils.getClientScopeMappings(scopedClient, scopeContainer); //scopedClient.getClientScopeMappings(client); List mapRep = new ArrayList(); @@ -101,14 +104,10 @@ public class ScopeMappedClientResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getAvailableClientScopeMappings() { - auth.requireView(); - - if (scopeContainer == null) { - throw new NotFoundException("Could not find client"); - } + viewPermission.require(); Set roles = scopedClient.getRoles(); - return ScopeMappedResource.getAvailable(scopeContainer, roles); + return ScopeMappedResource.getAvailable(auth, scopeContainer, roles); } /** @@ -123,11 +122,7 @@ public class ScopeMappedClientResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getCompositeClientScopeMappings() { - auth.requireView(); - - if (scopeContainer == null) { - throw new NotFoundException("Could not find client"); - } + viewPermission.require(); Set roles = scopedClient.getRoles(); return ScopeMappedResource.getComposite(scopeContainer, roles); @@ -141,11 +136,7 @@ public class ScopeMappedClientResource { @POST @Consumes(MediaType.APPLICATION_JSON) public void addClientScopeMapping(List roles) { - auth.requireManage(); - - if (scopeContainer == null) { - throw new NotFoundException("Could not find client"); - } + managePermission.require(); for (RoleRepresentation role : roles) { RoleModel roleModel = scopedClient.getRole(role.getName()); @@ -166,11 +157,7 @@ public class ScopeMappedClientResource { @DELETE @Consumes(MediaType.APPLICATION_JSON) public void deleteClientScopeMapping(List roles) { - auth.requireManage(); - - if (scopeContainer == null) { - throw new NotFoundException("Could not find client"); - } + managePermission.require(); if (roles == null) { Set roleModels = KeycloakModelUtils.getClientScopeMappings(scopedClient, scopeContainer);//scopedClient.getClientScopeMappings(client); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java index 19a32f9de7..286e22b5b5 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java @@ -31,6 +31,7 @@ import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.ClientMappingsRepresentation; import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -56,17 +57,25 @@ import java.util.Set; */ public class ScopeMappedResource { protected RealmModel realm; - private RealmAuth auth; + protected AdminPermissionEvaluator auth; + protected AdminPermissionEvaluator.RequirePermissionCheck managePermission; + protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission; + protected ScopeContainerModel scopeContainer; protected KeycloakSession session; protected AdminEventBuilder adminEvent; - public ScopeMappedResource(RealmModel realm, RealmAuth auth, ScopeContainerModel scopeContainer, KeycloakSession session, AdminEventBuilder adminEvent) { + public ScopeMappedResource(RealmModel realm, AdminPermissionEvaluator auth, ScopeContainerModel scopeContainer, + KeycloakSession session, AdminEventBuilder adminEvent, + AdminPermissionEvaluator.RequirePermissionCheck managePermission, + AdminPermissionEvaluator.RequirePermissionCheck viewPermission) { this.realm = realm; this.auth = auth; this.scopeContainer = scopeContainer; this.session = session; this.adminEvent = adminEvent.resource(ResourceType.REALM_SCOPE_MAPPING); + this.managePermission = managePermission; + this.viewPermission = viewPermission; } /** @@ -78,7 +87,7 @@ public class ScopeMappedResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public MappingsRepresentation getScopeMappings() { - auth.requireView(); + viewPermission.require(); if (scopeContainer == null) { throw new NotFoundException("Could not find client"); @@ -126,7 +135,7 @@ public class ScopeMappedResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getRealmScopeMappings() { - auth.requireView(); + viewPermission.require(); if (scopeContainer == null) { throw new NotFoundException("Could not find client"); @@ -150,20 +159,21 @@ public class ScopeMappedResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getAvailableRealmScopeMappings() { - auth.requireView(); + viewPermission.require(); if (scopeContainer == null) { throw new NotFoundException("Could not find client"); } Set roles = realm.getRoles(); - return getAvailable(scopeContainer, roles); + return getAvailable(auth, scopeContainer, roles); } - public static List getAvailable(ScopeContainerModel client, Set roles) { + public static List getAvailable(AdminPermissionEvaluator auth, ScopeContainerModel client, Set roles) { List available = new ArrayList(); for (RoleModel roleModel : roles) { if (client.hasScope(roleModel)) continue; + if (!auth.roles().canMapClientScope(roleModel)) continue; available.add(ModelToRepresentation.toRepresentation(roleModel)); } return available; @@ -183,7 +193,7 @@ public class ScopeMappedResource { @Produces(MediaType.APPLICATION_JSON) @NoCache public List getCompositeRealmScopeMappings() { - auth.requireView(); + viewPermission.require(); if (scopeContainer == null) { throw new NotFoundException("Could not find client"); @@ -210,7 +220,7 @@ public class ScopeMappedResource { @POST @Consumes(MediaType.APPLICATION_JSON) public void addRealmScopeMappings(List roles) { - auth.requireManage(); + managePermission.require(); if (scopeContainer == null) { throw new NotFoundException("Could not find client"); @@ -236,7 +246,7 @@ public class ScopeMappedResource { @DELETE @Consumes(MediaType.APPLICATION_JSON) public void deleteRealmScopeMappings(List roles) { - auth.requireManage(); + managePermission.require(); if (scopeContainer == null) { throw new NotFoundException("Could not find client"); @@ -268,6 +278,9 @@ public class ScopeMappedResource { @Path("clients/{client}") public ScopeMappedClientResource getClientByIdScopeMappings(@PathParam("client") String client) { ClientModel clientModel = realm.getClientById(client); - return new ScopeMappedClientResource(realm, auth, this.scopeContainer, session, clientModel, adminEvent); + if (clientModel == null) { + throw new NotFoundException("Could not find client"); + } + return new ScopeMappedClientResource(realm, auth, this.scopeContainer, session, clientModel, adminEvent, managePermission, viewPermission); } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java index 4ffcf86032..638f57ba6f 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java @@ -26,6 +26,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.UserStorageSyncManager; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.ldap.LDAPStorageProvider; @@ -55,7 +56,7 @@ public class UserStorageProviderResource { protected RealmModel realm; - protected RealmAuth auth; + protected AdminPermissionEvaluator auth; protected AdminEventBuilder adminEvent; @@ -71,12 +72,10 @@ public class UserStorageProviderResource { @Context protected HttpHeaders headers; - public UserStorageProviderResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) { + public UserStorageProviderResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.auth = auth; this.realm = realm; this.adminEvent = adminEvent; - - auth.init(RealmAuth.Resource.USER); } /** @@ -94,7 +93,7 @@ public class UserStorageProviderResource { @Produces(MediaType.APPLICATION_JSON) public SynchronizationResult syncUsers(@PathParam("id") String id, @QueryParam("action") String action) { - auth.requireManage(); + auth.users().requireManage(); ComponentModel model = realm.getComponent(id); if (model == null) { @@ -139,7 +138,7 @@ public class UserStorageProviderResource { @Path("{id}/remove-imported-users") @NoCache public void removeImportedUsers(@PathParam("id") String id) { - auth.requireManage(); + auth.users().requireManage(); ComponentModel model = realm.getComponent(id); if (model == null) { @@ -162,7 +161,7 @@ public class UserStorageProviderResource { @Path("{id}/unlink-users") @NoCache public void unlinkUsers(@PathParam("id") String id) { - auth.requireManage(); + auth.users().requireManage(); ComponentModel model = realm.getComponent(id); if (model == null) { @@ -187,7 +186,7 @@ public class UserStorageProviderResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public SynchronizationResult syncMapperData(@PathParam("parentId") String parentId, @PathParam("id") String mapperId, @QueryParam("direction") String direction) { - auth.requireManage(); + auth.users().requireManage(); ComponentModel parentModel = realm.getComponent(parentId); if (parentModel == null) throw new NotFoundException("Parent model not found"); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java index 2685e8ebf1..502fbe3ed0 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java @@ -22,8 +22,8 @@ import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.authentication.RequiredActionProvider; -import org.keycloak.authorization.admin.permissions.MgmtPermissions; import org.keycloak.authentication.actiontoken.execactions.ExecuteActionsActionToken; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.common.ClientConnection; import org.keycloak.common.Profile; import org.keycloak.common.util.Time; @@ -36,7 +36,6 @@ import org.keycloak.events.EventType; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.models.ClientModel; -import org.keycloak.models.ClientSessionModel; import org.keycloak.models.Constants; import org.keycloak.models.FederatedIdentityModel; import org.keycloak.models.GroupModel; @@ -116,7 +115,7 @@ public class UsersResource { protected RealmModel realm; - private RealmAuth auth; + private AdminPermissionEvaluator auth; private AdminEventBuilder adminEvent; @@ -132,12 +131,10 @@ public class UsersResource { @Context protected HttpHeaders headers; - public UsersResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) { + public UsersResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { this.auth = auth; this.realm = realm; this.adminEvent = adminEvent.resource(ResourceType.USER); - - auth.init(RealmAuth.Resource.USER); } /** @@ -151,13 +148,13 @@ public class UsersResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public Response updateUser(final @PathParam("id") String id, final UserRepresentation rep) { - auth.requireManage(); try { UserModel user = session.users().getUserById(id, realm); if (user == null) { return Response.status(Status.NOT_FOUND).build(); } + auth.users().requireManage(user); Set attrsToRemove; if (rep.getAttributes() != null) { @@ -188,6 +185,8 @@ public class UsersResource { } catch (ModelException me) { logger.warn("Could not update user!", me); return ErrorResponse.exists("Could not update user!"); + } catch (ForbiddenException fe) { + throw fe; } catch (Exception me) { // JPA logger.warn("Could not update user!", me);// may be committed by JTA which can't return ErrorResponse.exists("Could not update user!"); @@ -206,7 +205,7 @@ public class UsersResource { @POST @Consumes(MediaType.APPLICATION_JSON) public Response createUser(final @Context UriInfo uriInfo, final UserRepresentation rep) { - auth.requireManage(); + auth.users().requireManage(); // Double-check duplicated username and email here due to federation if (session.users().getUserByUsername(rep.getUsername(), realm) != null) { @@ -291,13 +290,13 @@ public class UsersResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public UserRepresentation getUser(final @PathParam("id") String id) { - auth.requireView(); - UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireView(user); + UserRepresentation rep = ModelToRepresentation.toRepresentation(session, realm, user); if (realm.isIdentityFederationEnabled()) { @@ -325,19 +324,17 @@ public class UsersResource { public Map impersonate(final @PathParam("id") String id) { ProfileHelper.requireFeature(Profile.Feature.IMPERSONATION); - auth.init(RealmAuth.Resource.IMPERSONATION); - auth.requireManage(); - UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } - RealmModel authenticatedRealm = auth.getAuth().getRealm(); + auth.users().requireImpersonate(user); + RealmModel authenticatedRealm = auth.adminAuth().getRealm(); // if same realm logout before impersonation boolean sameRealm = false; if (authenticatedRealm.getId().equals(realm.getId())) { sameRealm = true; - UserSessionModel userSession = session.sessions().getUserSession(authenticatedRealm, auth.getAuth().getToken().getSessionState()); + UserSessionModel userSession = session.sessions().getUserSession(authenticatedRealm, auth.adminAuth().getToken().getSessionState()); AuthenticationManager.expireIdentityCookie(realm, uriInfo, clientConnection); AuthenticationManager.expireRememberMeCookie(realm, uriInfo, clientConnection); AuthenticationManager.backchannelLogout(session, authenticatedRealm, userSession, uriInfo, clientConnection, headers, true); @@ -355,7 +352,7 @@ public class UsersResource { .session(userSession) .user(user) .detail(Details.IMPERSONATOR_REALM,authenticatedRealm.getName()) - .detail(Details.IMPERSONATOR, auth.getAuth().getUser().getUsername()).success(); + .detail(Details.IMPERSONATOR, auth.adminAuth().getUser().getUsername()).success(); return result; } @@ -372,12 +369,11 @@ public class UsersResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List getSessions(final @PathParam("id") String id) { - auth.requireView(); - UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireView(user); List sessions = session.sessions().getUserSessions(realm, user); List reps = new ArrayList(); for (UserSessionModel session : sessions) { @@ -398,12 +394,11 @@ public class UsersResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List getOfflineSessions(final @PathParam("id") String id, final @PathParam("clientId") String clientId) { - auth.requireView(); - UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireView(user); ClientModel client = realm.getClientById(clientId); if (client == null) { throw new NotFoundException("Client not found"); @@ -439,12 +434,11 @@ public class UsersResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List getFederatedIdentity(final @PathParam("id") String id) { - auth.requireView(); - UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireView(user); return getFederatedIdentities(user); } @@ -476,12 +470,12 @@ public class UsersResource { @POST @NoCache public Response addFederatedIdentity(final @PathParam("id") String id, final @PathParam("provider") String provider, FederatedIdentityRepresentation rep) { - auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireManage(user); if (session.users().getFederatedIdentity(user, provider, realm) != null) { return ErrorResponse.exists("User is already linked with provider"); } @@ -502,12 +496,11 @@ public class UsersResource { @DELETE @NoCache public void removeFederatedIdentity(final @PathParam("id") String id, final @PathParam("provider") String provider) { - auth.requireManage(); - UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireManage(user); if (!session.users().removeFederatedIdentity(realm, user, provider)) { throw new NotFoundException("Link not found"); } @@ -525,13 +518,11 @@ public class UsersResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List> getConsents(final @PathParam("id") String id) { - auth.requireView(); - UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } - + auth.users().requireView(user); List> result = new LinkedList<>(); Set offlineClients = new UserSessionManager(session).findClientsWithOfflineToken(realm, user); @@ -580,14 +571,16 @@ public class UsersResource { @DELETE @NoCache public void revokeConsent(final @PathParam("id") String id, final @PathParam("client") String clientId) { - auth.requireManage(); - UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireManage(user); ClientModel client = realm.getClientByClientId(clientId); + if (client == null) { + throw new NotFoundException("Client not found"); + } boolean revokedConsent = session.users().revokeConsentForClient(realm, user.getId(), client.getId()); boolean revokedOfflineToken = new UserSessionManager(session).revokeOfflineToken(user, client); @@ -612,12 +605,11 @@ public class UsersResource { @Path("{id}/logout") @POST public void logout(final @PathParam("id") String id) { - auth.requireManage(); - UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireManage(user); List userSessions = session.sessions().getUserSessions(realm, user); for (UserSessionModel userSession : userSessions) { @@ -635,12 +627,11 @@ public class UsersResource { @DELETE @NoCache public Response deleteUser(final @PathParam("id") String id) { - auth.requireManage(); - UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireManage(user); boolean removed = new UserManager(session).removeUser(realm, user); if (removed) { @@ -675,7 +666,7 @@ public class UsersResource { @QueryParam("username") String username, @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) { - auth.requireView(); + auth.users().requireQuery(); firstResult = firstResult != null ? firstResult : -1; maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS; @@ -703,7 +694,9 @@ public class UsersResource { userModels = session.users().getUsers(realm, firstResult, maxResults, false); } + boolean canViewGlobal = auth.users().canView(); for (UserModel user : userModels) { + if (!canViewGlobal && !auth.users().canView(user)) continue; results.add(ModelToRepresentation.toRepresentation(session, realm, user)); } return results; @@ -714,21 +707,23 @@ public class UsersResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public Integer getUsersCount() { - auth.requireView(); + auth.users().requireView(); return session.users().getUsersCount(realm); } @Path("{id}/role-mappings") public RoleMapperResource getRoleMappings(@PathParam("id") String id) { - auth.init(RealmAuth.Resource.USER); UserModel user = session.users().getUserById(id, realm); - RoleMapperResource resource = new RoleMapperResource(realm, auth, user, adminEvent); - resource.setManageCheck(() -> { - return new MgmtPermissions(session, realm, auth.getAuth()).users().canManage(); - }); + if (user == null) { + throw new NotFoundException("User not found"); + } + + AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.users().requireManage(user); + AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.users().requireView(user); + RoleMapperResource resource = new RoleMapperResource(realm, auth, user, adminEvent, manageCheck, viewCheck); ResteasyProviderFactory.getInstance().injectProperties(resource); return resource; @@ -744,12 +739,12 @@ public class UsersResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public void disableCredentialType(@PathParam("id") String id, List credentialTypes) { - auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireManage(user); if (credentialTypes == null) return; for (String type : credentialTypes) { session.userCredentialManager().disableCredentialType(realm, user, type); @@ -771,12 +766,12 @@ public class UsersResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public void resetPassword(@PathParam("id") String id, CredentialRepresentation pass) { - auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireManage(user); if (pass == null || pass.getValue() == null || !CredentialRepresentation.PASSWORD.equals(pass.getType())) { throw new BadRequestException("No password provided"); } @@ -792,7 +787,7 @@ public class UsersResource { } catch (ReadOnlyException mre) { throw new BadRequestException("Can't reset password as account is read only"); } catch (ModelException e) { - Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); + Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale()); throw new ErrorResponseException(e.getMessage(), MessageFormat.format(messages.getProperty(e.getMessage(), e.getMessage()), e.getParameters()), Status.BAD_REQUEST); } @@ -810,13 +805,15 @@ public class UsersResource { @PUT @Consumes(MediaType.APPLICATION_JSON) public void removeTotp(@PathParam("id") String id) { - auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireManage(user); + + session.userCredentialManager().disableCredentialType(realm, user, CredentialModel.OTP); adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success(); } @@ -870,12 +867,12 @@ public class UsersResource { @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId, @QueryParam("lifespan") Integer lifespan, List actions) { - auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { return ErrorResponse.error("User not found", Response.Status.NOT_FOUND); } + auth.users().requireManage(user); if (user.getEmail() == null) { return ErrorResponse.error("User email missing", Response.Status.BAD_REQUEST); @@ -964,12 +961,12 @@ public class UsersResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public List groupMembership(@PathParam("id") String id) { - auth.requireView(); UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireView(user); List memberships = new LinkedList<>(); for (GroupModel group : user.getGroups()) { memberships.add(ModelToRepresentation.toRepresentation(group, false)); @@ -981,12 +978,13 @@ public class UsersResource { @Path("{id}/groups/{groupId}") @NoCache public void removeMembership(@PathParam("id") String id, @PathParam("groupId") String groupId) { - auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireManage(user); + GroupModel group = session.realms().getGroupById(groupId, realm); if (group == null) { throw new NotFoundException("Group not found"); @@ -998,7 +996,7 @@ public class UsersResource { adminEvent.operation(OperationType.DELETE).resource(ResourceType.GROUP_MEMBERSHIP).representation(ModelToRepresentation.toRepresentation(group, true)).resourcePath(uriInfo).success(); } } catch (ModelException me) { - Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); + Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale()); throw new ErrorResponseException(me.getMessage(), MessageFormat.format(messages.getProperty(me.getMessage(), me.getMessage()), me.getParameters()), Response.Status.BAD_REQUEST); } @@ -1008,12 +1006,12 @@ public class UsersResource { @Path("{id}/groups/{groupId}") @NoCache public void joinGroup(@PathParam("id") String id, @PathParam("groupId") String groupId) { - auth.requireManage(); UserModel user = session.users().getUserById(id, realm); if (user == null) { throw new NotFoundException("User not found"); } + auth.users().requireManage(user); GroupModel group = session.realms().getGroupById(groupId, realm); if (group == null) { throw new NotFoundException("Group not found"); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java new file mode 100644 index 0000000000..56be4cf588 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java @@ -0,0 +1,55 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.services.resources.admin.AdminAuth; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface AdminPermissionEvaluator { + RealmPermissionEvaluator realm(); + + AdminAuth adminAuth(); + + RolePermissionEvaluator roles(); + UserPermissionEvaluator users(); + ClientPermissionEvaluator clients(); + GroupPermissionEvaluator groups(); + + /** + * Useful as a function pointer, i.e. RoleMapperResource is reused bewteen GroupResource and UserResource to manage role mappings. + * We don't know what type of resource we're managing here (user or group), so we don't know how to query the policy engine to determine + * if an action is allowed. + * + */ + interface PermissionCheck { + boolean evaluate(); + } + /** + * Useful as a function pointer, i.e. RoleMapperResource is reused bewteen GroupResource and UserResource to manage role mappings. + * We don't know what type of resource we're managing here (user or group), so we don't know how to query the policy engine to determine + * if an action is allowed. + * + * throws appropriate exception if permission is deny + * + */ + interface RequirePermissionCheck { + void require(); + } +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionManagement.java new file mode 100644 index 0000000000..1f0a34dd91 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionManagement.java @@ -0,0 +1,35 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.authorization.model.ResourceServer; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface AdminPermissionManagement { + public static final String MANAGE_SCOPE = "manage"; + public static final String VIEW_SCOPE = "view"; + + RolePermissionManagement roles(); + UserPermissionManagement users(); + GroupPermissionManagement groups(); + ClientPermissionManagement clients(); + + ResourceServer realmResourceServer(); +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java new file mode 100644 index 0000000000..61a8bffe5e --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.resources.admin.AdminAuth; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class AdminPermissions { + + + public static AdminPermissionEvaluator evaluator(KeycloakSession session, RealmModel realm, AdminAuth auth) { + return new MgmtPermissions(session, realm, auth); + } + public static AdminPermissionEvaluator evaluator(KeycloakSession session, RealmModel realm, RealmModel adminsRealm, UserModel admin) { + return new MgmtPermissions(session, realm, adminsRealm, admin); + } + + public static AdminPermissionManagement management(KeycloakSession session, RealmModel realm) { + return new MgmtPermissions(session, realm); + } + + +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java new file mode 100644 index 0000000000..8f0af4b93c --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java @@ -0,0 +1,68 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientTemplateModel; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface ClientPermissionEvaluator { + boolean isPermissionsEnabled(ClientModel client); + + void setPermissionsEnabled(ClientModel client, boolean enable); + + boolean canManage(); + + void requireManage(); + + boolean canManageTemplates(); + + void requireManageTemplates(); + + boolean canView(); + + boolean canList(); + + boolean canViewTemplates(); + + void requireList(); + + boolean canListTemplates(); + + void requireView(); + + void requireViewTemplates(); + + boolean canManage(ClientModel client); + + void requireManage(ClientModel client); + + boolean canView(ClientModel client); + + void requireView(ClientModel client); + + boolean canManage(ClientTemplateModel template); + + void requireManage(ClientTemplateModel template); + + boolean canView(ClientTemplateModel template); + + void requireView(ClientTemplateModel template); +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java new file mode 100644 index 0000000000..27f55272e8 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java @@ -0,0 +1,29 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.models.ClientModel; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface ClientPermissionManagement { + boolean isPermissionsEnabled(ClientModel client); + + void setPermissionsEnabled(ClientModel client, boolean enable); +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java new file mode 100644 index 0000000000..da1d24be53 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java @@ -0,0 +1,309 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.jboss.logging.Logger; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.models.AdminRoles; +import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientTemplateModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.services.ForbiddenException; + +import java.util.HashSet; +import java.util.Set; + +/** + * Manages default policies for all users. + * + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionManagement { + private static final Logger logger = Logger.getLogger(ClientPermissions.class); + protected final KeycloakSession session; + protected final RealmModel realm; + protected final AuthorizationProvider authz; + protected final MgmtPermissions root; + + public ClientPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) { + this.session = session; + this.realm = realm; + this.authz = authz; + this.root = root; + } + + private String getResourceName(ClientModel client) { + return "group.resource." + client.getId(); + } + + private String getManagePermissionName(ClientModel client) { + return "manage.permission.client." + client.getId(); + } + private String getViewPermissionName(ClientModel client) { + return "view.permission.client." + client.getId(); + } + + private void initialize(ClientModel client) { + ResourceServer server = root.findOrCreateResourceServer(client); + Scope manageScope = manageScope(server); + if (manageScope == null) { + authz.getStoreFactory().getScopeStore().create(AdminPermissionManagement.MANAGE_SCOPE, server); + } + Scope viewScope = viewScope(server); + if (manageScope == null) { + authz.getStoreFactory().getScopeStore().create(AdminPermissionManagement.VIEW_SCOPE, server); + } + + String resourceName = getResourceName(client); + Resource resource = authz.getStoreFactory().getResourceStore().findByName(resourceName, server.getId()); + if (resource == null) { + resource = authz.getStoreFactory().getResourceStore().create(resourceName, server, server.getClientId()); + Set scopeset = new HashSet<>(); + scopeset.add(manageScope); + scopeset.add(viewScope); + resource.updateScopes(scopeset); + } + String managePermissionName = getManagePermissionName(client); + Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(managePermissionName, server.getId()); + if (managePermission == null) { + RoleModel role = root.getRealmManagementClient().getRole(AdminRoles.MANAGE_CLIENTS); + Policy manageClientsPolicy = root.roles().rolePolicy(server, role); + Helper.addScopePermission(authz, server, managePermissionName, resource, manageScope, manageClientsPolicy); + } + String viewPermissionName = getViewPermissionName(client); + Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(viewPermissionName, server.getId()); + if (viewPermission == null) { + RoleModel role = root.getRealmManagementClient().getRole(AdminRoles.VIEW_CLIENTS); + Policy viewClientsPolicy = root.roles().rolePolicy(server, role); + Helper.addScopePermission(authz, server, viewPermissionName, resource, viewScope, viewClientsPolicy); + } + } + + private void deletePermissions(ClientModel client) { + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) return; + Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(getManagePermissionName(client), server.getId()); + if (managePermission != null) { + authz.getStoreFactory().getPolicyStore().delete(managePermission.getId()); + } + Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(getViewPermissionName(client), server.getId()); + if (viewPermission != null) { + authz.getStoreFactory().getPolicyStore().delete(viewPermission.getId()); + } + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());; + if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId()); + } + + @Override + public boolean isPermissionsEnabled(ClientModel client) { + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) return false; + + return authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId()) != null; + } + + @Override + public void setPermissionsEnabled(ClientModel client, boolean enable) { + if (enable) { + initialize(client); + } else { + deletePermissions(client); + } + } + + + + private Scope manageScope(ResourceServer server) { + return authz.getStoreFactory().getScopeStore().findByName(AdminPermissionManagement.MANAGE_SCOPE, server.getId()); + } + + private Scope viewScope(ResourceServer server) { + return authz.getStoreFactory().getScopeStore().findByName(AdminPermissionManagement.VIEW_SCOPE, server.getId()); + } + + @Override + public boolean canList() { + return root.hasAnyAdminRole(); + } + + @Override + public void requireList() { + if (!canList()) { + throw new ForbiddenException(); + } + } + + @Override + public boolean canListTemplates() { + return root.hasAnyAdminRole(); + } + + public boolean canManageClientDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_CLIENTS); + } + public boolean canViewClientDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_CLIENTS, AdminRoles.VIEW_CLIENTS); + } + + @Override + public boolean canManage() { + return canManageClientDefault(); + } + + @Override + public void requireManage() { + if (!canManage()) { + throw new ForbiddenException(); + } + } + @Override + public boolean canView() { + return canManageClientDefault() || canViewClientDefault(); + } + + @Override + public void requireView() { + if (!canView()) { + throw new ForbiddenException(); + } + } + + @Override + public boolean canManage(ClientModel client) { + if (!root.isAdminSameRealm()) { + return canManage(); + } + + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) return canManage(); + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId()); + if (resource == null) return canManage(); + + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getManagePermissionName(client), server.getId()); + if (policy == null) { + return canManage(); + } + + Set associatedPolicies = policy.getAssociatedPolicies(); + // if no policies attached to permission then just do default behavior + if (associatedPolicies == null || associatedPolicies.isEmpty()) { + return canManage(); + } + + Scope scope = manageScope(server); + return root.evaluatePermission(resource, scope, server); + } + + @Override + public void requireManage(ClientModel client) { + if (!canManage(client)) { + throw new ForbiddenException(); + } + } + + @Override + public boolean canView(ClientModel client) { + if (!root.isAdminSameRealm()) { + return canView(); + } + + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) return canView(); + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId()); + if (resource == null) return canView(); + + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getViewPermissionName(client), server.getId()); + if (policy == null) { + return canView(); + } + + Set associatedPolicies = policy.getAssociatedPolicies(); + // if no policies attached to permission then just do default behavior + if (associatedPolicies == null || associatedPolicies.isEmpty()) { + return canView(); + } + + Scope scope = viewScope(server); + return root.evaluatePermission(resource, scope, server); + } + + @Override + public void requireView(ClientModel client) { + if (!canView(client)) { + throw new ForbiddenException(); + } + } + + // templates + + @Override + public boolean canViewTemplates() { + return canView(); + } + + @Override + public boolean canManageTemplates() { + return canManageClientDefault(); + } + + @Override + public void requireManageTemplates() { + if (!canManageTemplates()) { + throw new ForbiddenException(); + } + } + @Override + public void requireViewTemplates() { + if (!canViewTemplates()) { + throw new ForbiddenException(); + } + } + + @Override + public boolean canManage(ClientTemplateModel template) { + return canManageClientDefault(); + } + + @Override + public void requireManage(ClientTemplateModel template) { + if (!canManage(template)) { + throw new ForbiddenException(); + } + } + + @Override + public boolean canView(ClientTemplateModel template) { + return canViewClientDefault(); + } + + @Override + public void requireView(ClientTemplateModel template) { + if (!canView(template)) { + throw new ForbiddenException(); + } + } +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java new file mode 100644 index 0000000000..91ca5c5e9d --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.models.GroupModel; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface GroupPermissionEvaluator { + boolean canList(); + + void requireList(); + + boolean canManage(GroupModel group); + + void requireManage(GroupModel group); + + boolean canView(GroupModel group); + + void requireView(GroupModel group); + + boolean canManage(); + + void requireManage(); + + boolean canView(); + + void requireView(); + + boolean canViewMembers(GroupModel group); + + void requireViewMembers(GroupModel group); + + boolean canManageMembers(GroupModel group); + + void requireManageMembers(GroupModel group); +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java new file mode 100644 index 0000000000..5f4097be27 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.authorization.model.Policy; +import org.keycloak.models.GroupModel; +import org.keycloak.models.RoleModel; + +import java.util.Map; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface GroupPermissionManagement { + boolean isPermissionsEnabled(GroupModel group); + void setPermissionsEnabled(GroupModel group, boolean enable); + + Policy viewMembersPermission(GroupModel group); + Policy manageMembersPermission(GroupModel group); + Policy viewPermissionGroup(GroupModel group); + Policy managePermissionGroup(GroupModel group); +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java new file mode 100644 index 0000000000..46d8fb63fe --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java @@ -0,0 +1,383 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.jboss.logging.Logger; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.models.AdminRoles; +import org.keycloak.models.GroupModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.services.ForbiddenException; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManagement { + private static final Logger logger = Logger.getLogger(GroupPermissions.class); + public static final String MAP_ROLE_SCOPE = "map-role"; + public static final String MANAGE_MEMBERS_SCOPE = "manage.members"; + public static final String VIEW_MEMBERS_SCOPE = "view.members"; + protected final KeycloakSession session; + protected final RealmModel realm; + protected final AuthorizationProvider authz; + protected final MgmtPermissions root; + + public GroupPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) { + this.session = session; + this.realm = realm; + this.authz = authz; + this.root = root; + } + + private static String getGroupResourceName(GroupModel group) { + return "group.resource." + getGroupSuffix(group); + } + + + public static String getManagePermissionGroup(GroupModel group) { + return "manage.permission.group." + getGroupSuffix(group); + } + + public static String getManageMembersPermissionGroup(GroupModel group) { + return "manage.members.permission.group." + getGroupSuffix(group); + } + + public static String getGroupSuffix(GroupModel group) { + return ModelToRepresentation.buildGroupPath(group).replace('/', '.'); + } + + public static String getViewPermissionGroup(GroupModel group) { + return "view.permission.group." + getGroupSuffix(group); + } + + public static String getViewMembersPermissionGroup(GroupModel group) { + return "view.members.permission.group." + getGroupSuffix(group); + } + + private void initialize(GroupModel group) { + root.initializeRealmResourceServer(); + root.initializeRealmDefaultScopes(); + ResourceServer server = root.realmResourceServer(); + Scope manageScope = root.realmManageScope(); + Scope viewScope = root.realmViewScope(); + Scope manageMembersScope = root.initializeRealmScope(MANAGE_MEMBERS_SCOPE); + Scope viewMembersScope = root.initializeRealmScope(VIEW_MEMBERS_SCOPE); + + String groupResourceName = getGroupResourceName(group); + Resource groupResource = authz.getStoreFactory().getResourceStore().findByName(groupResourceName, server.getId()); + if (groupResource == null) { + groupResource = authz.getStoreFactory().getResourceStore().create(groupResourceName, server, server.getClientId()); + Set scopeset = new HashSet<>(); + scopeset.add(manageScope); + scopeset.add(viewScope); + groupResource.updateScopes(scopeset); + } + String managePermissionName = getManagePermissionGroup(group); + Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(managePermissionName, server.getId()); + if (managePermission == null) { + Policy manageUsersPolicy = root.roles().manageUsersPolicy(server); + Helper.addScopePermission(authz, server, managePermissionName, groupResource, manageScope, manageUsersPolicy); + } + String viewPermissionName = getManagePermissionGroup(group); + Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(viewPermissionName, server.getId()); + if (viewPermission == null) { + Policy viewUsersPolicy = root.roles().viewUsersPolicy(server); + Helper.addScopePermission(authz, server, viewPermissionName, groupResource, viewScope, viewUsersPolicy); + } + String manageMembersPermissionName = getManageMembersPermissionGroup(group); + Policy manageMembersPermission = authz.getStoreFactory().getPolicyStore().findByName(manageMembersPermissionName, server.getId()); + if (manageMembersPermission == null) { + Policy manageUsersPolicy = root.roles().manageUsersPolicy(server); + Helper.addScopePermission(authz, server, manageMembersPermissionName, groupResource, manageMembersScope, manageUsersPolicy); + } + String viewMembersPermissionName = getViewMembersPermissionGroup(group); + Policy viewMembersPermission = authz.getStoreFactory().getPolicyStore().findByName(viewMembersPermissionName, server.getId()); + if (viewMembersPermission == null) { + Policy viewUsersPolicy = root.roles().viewUsersPolicy(server); + Helper.addScopePermission(authz, server, viewMembersPermissionName, groupResource, viewMembersScope, viewUsersPolicy); + } + } + + @Override + public boolean canList() { + return root.hasOneAdminRole(AdminRoles.VIEW_USERS, AdminRoles.MANAGE_USERS); + } + + @Override + public void requireList() { + if (!canList()) { + throw new ForbiddenException(); + } + } + + + + @Override + public boolean isPermissionsEnabled(GroupModel group) { + ResourceServer server = root.realmResourceServer(); + if (server == null) return false; + + return authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId()) != null; + } + + private Resource groupResource(GroupModel group) { + ResourceServer server = root.realmResourceServer(); + if (server == null) return null; + String groupResourceName = getGroupResourceName(group); + return authz.getStoreFactory().getResourceStore().findByName(groupResourceName, server.getId()); + } + + @Override + public void setPermissionsEnabled(GroupModel group, boolean enable) { + if (enable) { + initialize(group); + } else { + deletePermissions(group); + } + } + + private void deletePermissions(GroupModel group) { + ResourceServer server = root.realmResourceServer(); + if (server == null) return; + Policy managePermission = managePermissionGroup(group); + if (managePermission != null) { + authz.getStoreFactory().getPolicyStore().delete(managePermission.getId()); + } + Policy viewPermission = viewPermissionGroup(group); + if (viewPermission != null) { + authz.getStoreFactory().getPolicyStore().delete(viewPermission.getId()); + } + Policy manageMembersPermission = manageMembersPermission(group); + if (manageMembersPermission != null) { + authz.getStoreFactory().getPolicyStore().delete(manageMembersPermission.getId()); + } + Policy viewMembersPermission = viewMembersPermission(group); + if (manageMembersPermission == null) { + authz.getStoreFactory().getPolicyStore().delete(viewMembersPermission.getId()); + } + Resource resource = groupResource(group); + if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId()); + } + + @Override + public Policy viewMembersPermission(GroupModel group) { + ResourceServer server = root.realmResourceServer(); + if (server == null) return null; + String viewMembersPermissionName = getViewMembersPermissionGroup(group); + return authz.getStoreFactory().getPolicyStore().findByName(viewMembersPermissionName, server.getId()); + } + + @Override + public Policy manageMembersPermission(GroupModel group) { + ResourceServer server = root.realmResourceServer(); + if (server == null) return null; + String manageMembersPermissionName = getManageMembersPermissionGroup(group); + return authz.getStoreFactory().getPolicyStore().findByName(manageMembersPermissionName, server.getId()); + } + + @Override + public Policy viewPermissionGroup(GroupModel group) { + ResourceServer server = root.realmResourceServer(); + if (server == null) return null; + String viewPermissionName = getViewPermissionGroup(group); + return authz.getStoreFactory().getPolicyStore().findByName(viewPermissionName, server.getId()); + } + + @Override + public Policy managePermissionGroup(GroupModel group) { + ResourceServer server = root.realmResourceServer(); + if (server == null) return null; + String managePermissionName = getManagePermissionGroup(group); + return authz.getStoreFactory().getPolicyStore().findByName(managePermissionName, server.getId()); + } + + + @Override + public boolean canManage(GroupModel group) { + if (!root.isAdminSameRealm()) { + return canManage(); + } + + ResourceServer server = root.realmResourceServer(); + if (server == null) return canManage(); + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId()); + if (resource == null) return canManage(); + + Policy policy = managePermissionGroup(group); + if (policy == null) { + return canManage(); + } + + Set associatedPolicies = policy.getAssociatedPolicies(); + // if no policies attached to permission then just do default behavior + if (associatedPolicies == null || associatedPolicies.isEmpty()) { + return canManage(); + } + + Scope scope = root.realmManageScope(); + return root.evaluatePermission(resource, scope, server); + } + + @Override + public void requireManage(GroupModel group) { + if (!canManage(group)) { + throw new ForbiddenException(); + } + } + @Override + public boolean canView(GroupModel group) { + if (!root.isAdminSameRealm()) { + return canView(); + } + + ResourceServer server = root.realmResourceServer(); + if (server == null) return canView(); + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId()); + if (resource == null) return canView(); + + Policy policy = viewPermissionGroup(group); + if (policy == null) { + return canView(); + } + + Set associatedPolicies = policy.getAssociatedPolicies(); + // if no policies attached to permission then abort + if (associatedPolicies == null || associatedPolicies.isEmpty()) { + return canView(); + } + + Scope scope = root.realmViewScope(); + return root.evaluatePermission(resource, scope, server); + } + + @Override + public void requireView(GroupModel group) { + if (!canView(group)) { + throw new ForbiddenException(); + } + } + + @Override + public boolean canManage() { + return root.users().canManageDefault(); + } + + @Override + public void requireManage() { + if (!canManage()) { + throw new ForbiddenException(); + } + } + @Override + public boolean canView() { + return root.users().canViewDefault(); + } + + @Override + public void requireView() { + if (!canView()) { + throw new ForbiddenException(); + } + } + + + + @Override + public boolean canViewMembers(GroupModel group) { + if (!root.isAdminSameRealm()) { + return root.users().canView(); + } + + ResourceServer server = root.realmResourceServer(); + if (server == null) return root.users().canView(); + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId()); + if (resource == null) return root.users().canView(); + + Policy policy = viewMembersPermission(group); + if (policy == null) { + return root.users().canView(); + } + + Set associatedPolicies = policy.getAssociatedPolicies(); + // if no policies attached to permission then just do default behavior + if (associatedPolicies == null || associatedPolicies.isEmpty()) { + return root.users().canView(); + } + + Scope scope = authz.getStoreFactory().getScopeStore().findByName(VIEW_MEMBERS_SCOPE, server.getId()); + + return root.evaluatePermission(resource, scope, server); + } + + @Override + public void requireViewMembers(GroupModel group) { + if (!canViewMembers(group)) { + throw new ForbiddenException(); + } + } + + + @Override + public boolean canManageMembers(GroupModel group) { + if (!root.isAdminSameRealm()) { + return root.users().canManage(); + } + + ResourceServer server = root.realmResourceServer(); + if (server == null) return root.users().canManage(); + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId()); + if (resource == null) return root.users().canManage(); + + Policy policy = manageMembersPermission(group); + if (policy == null) { + return root.users().canManage(); + } + + Set associatedPolicies = policy.getAssociatedPolicies(); + // if no policies attached to permission then just do default behavior + if (associatedPolicies == null || associatedPolicies.isEmpty()) { + return root.users().canManage(); + } + + Scope scope = authz.getStoreFactory().getScopeStore().findByName(MANAGE_MEMBERS_SCOPE, server.getId()); + return root.evaluatePermission(resource, scope, server); + } + + @Override + public void requireManageMembers(GroupModel group) { + if (!canManageMembers(group)) { + throw new ForbiddenException(); + } + } + + + +} diff --git a/services/src/main/java/org/keycloak/authorization/admin/permissions/Helper.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/Helper.java similarity index 98% rename from services/src/main/java/org/keycloak/authorization/admin/permissions/Helper.java rename to services/src/main/java/org/keycloak/services/resources/admin/permissions/Helper.java index a45ea7f79a..2e7942d640 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/permissions/Helper.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/Helper.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.authorization.admin.permissions; +package org.keycloak.services.resources.admin.permissions; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.model.Policy; @@ -35,7 +35,7 @@ import java.util.Map; * @author Bill Burke * @version $Revision: 1 $ */ -public class Helper { +class Helper { public static Policy addScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope, Policy policy) { ScopePermissionRepresentation representation = new ScopePermissionRepresentation(); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java new file mode 100644 index 0000000000..943428364d --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java @@ -0,0 +1,274 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.jboss.logging.Logger; +import org.keycloak.Config; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.AuthorizationProviderFactory; +import org.keycloak.authorization.Decision; +import org.keycloak.authorization.common.DefaultEvaluationContext; +import org.keycloak.authorization.common.KeycloakIdentity; +import org.keycloak.authorization.common.UserModelIdentity; +import org.keycloak.authorization.identity.Identity; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.authorization.permission.ResourcePermission; +import org.keycloak.authorization.permission.evaluator.PermissionEvaluator; +import org.keycloak.authorization.policy.evaluation.DecisionResult; +import org.keycloak.authorization.policy.evaluation.EvaluationContext; +import org.keycloak.authorization.store.ResourceServerStore; +import org.keycloak.authorization.util.Permissions; +import org.keycloak.models.AdminRoles; +import org.keycloak.models.ClientModel; +import org.keycloak.models.Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.ForbiddenException; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.resources.admin.AdminAuth; + +import java.util.List; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManagement { + private static final Logger logger = Logger.getLogger(MgmtPermissions.class); + + protected RealmModel realm; + protected KeycloakSession session; + protected AuthorizationProvider authz; + protected AdminAuth auth; + protected Identity identity; + protected UserModel admin; + protected RealmModel adminsRealm; + protected ResourceServer realmResourceServer; + protected UserPermissions users; + protected GroupPermissions groups; + protected RealmPermissions realmPermissions; + protected ClientPermissions clientPermissions; + + + MgmtPermissions(KeycloakSession session, RealmModel realm) { + this.session = session; + this.realm = realm; + KeycloakSessionFactory keycloakSessionFactory = session.getKeycloakSessionFactory(); + AuthorizationProviderFactory factory = (AuthorizationProviderFactory) keycloakSessionFactory.getProviderFactory(AuthorizationProvider.class); + this.authz = factory.create(session, realm); + } + + MgmtPermissions(KeycloakSession session, RealmModel realm, AdminAuth auth) { + this(session, realm); + this.auth = auth; + this.admin = auth.getUser(); + this.adminsRealm = auth.getRealm(); + if (!auth.getRealm().equals(realm) + && !auth.getRealm().equals(new RealmManager(session).getKeycloakAdminstrationRealm())) { + throw new ForbiddenException(); + } + if (auth.getClient().getClientId().equals(Constants.ADMIN_CLI_CLIENT_ID)) { + this.identity = new UserModelIdentity(auth.getRealm(), auth.getUser()); + + } else { + this.identity = new KeycloakIdentity(auth.getToken(), session); + } + } + MgmtPermissions(KeycloakSession session, RealmModel realm, RealmModel adminsRealm, UserModel admin) { + this(session, realm); + this.admin = admin; + this.adminsRealm = adminsRealm; + this.identity = new UserModelIdentity(realm, admin); + } + + public ClientModel getRealmManagementClient() { + ClientModel client = null; + if (realm.getName().equals(Config.getAdminRealm())) { + client = realm.getClientByClientId(Config.getAdminRealm() + "-realm"); + } else { + client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); + + } + return client; + } + + + + public boolean hasAnyAdminRole() { + return hasOneAdminRole(AdminRoles.ALL_REALM_ROLES); + } + + public boolean hasOneAdminRole(String... adminRoles) { + String clientId; + RealmManager realmManager = new RealmManager(session); + if (adminsRealm.equals(realmManager.getKeycloakAdminstrationRealm())) { + clientId = realm.getMasterAdminClient().getClientId(); + } else { + clientId = realm.getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm())).getClientId(); + } + for (String adminRole : adminRoles) { + if (identity.hasClientRole(clientId, adminRole)) return true; + } + return false; + } + + + + public boolean isAdminSameRealm() { + return auth == null || realm.getId().equals(auth.getRealm().getId()); + } + + @Override + public AdminAuth adminAuth() { + return auth; + } + + public Identity identity() { + return identity; + } + + public UserModel admin() { + return admin; + } + + + @Override + public RolePermissions roles() { + return new RolePermissions(session, realm, authz, this); + } + + @Override + public UserPermissions users() { + if (users != null) return users; + users = new UserPermissions(session, realm, authz, this); + return users; + } + + @Override + public RealmPermissions realm() { + if (realmPermissions != null) return realmPermissions; + realmPermissions = new RealmPermissions(session, realm, authz, this); + return realmPermissions; + } + + @Override + public ClientPermissions clients() { + if (clientPermissions != null) return clientPermissions; + clientPermissions = new ClientPermissions(session, realm, authz, this); + return clientPermissions; + } + + @Override + public GroupPermissions groups() { + if (groups != null) return groups; + groups = new GroupPermissions(session, realm, authz, this); + return groups; + } + + public ResourceServer findOrCreateResourceServer(ClientModel client) { + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) { + server = authz.getStoreFactory().getResourceServerStore().create(client.getId()); + } + return server; + } + + @Override + public ResourceServer realmResourceServer() { + if (realmResourceServer != null) return realmResourceServer; + ResourceServerStore resourceServerStore = authz.getStoreFactory().getResourceServerStore(); + ClientModel client = getRealmManagementClient(); + realmResourceServer = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + return realmResourceServer; + + } + + public ResourceServer initializeRealmResourceServer() { + if (realmResourceServer != null) return realmResourceServer; + ClientModel client = getRealmManagementClient(); + return findOrCreateResourceServer(client); + } + + protected Scope manageScope; + protected Scope viewScope; + + public void initializeRealmDefaultScopes() { + ResourceServer server = initializeRealmResourceServer(); + manageScope = initializeRealmScope(MgmtPermissions.MANAGE_SCOPE); + viewScope = initializeRealmScope(MgmtPermissions.VIEW_SCOPE); + } + + public Scope initializeRealmScope(String name) { + ResourceServer server = initializeRealmResourceServer(); + Scope scope = authz.getStoreFactory().getScopeStore().findByName(name, server.getId()); + if (scope == null) { + scope = authz.getStoreFactory().getScopeStore().create(name, server); + } + return scope; + } + + + + public Scope realmManageScope() { + if (manageScope != null) return manageScope; + manageScope = realmScope(MgmtPermissions.MANAGE_SCOPE); + return manageScope; + } + + + public Scope realmViewScope() { + if (viewScope != null) return viewScope; + viewScope = realmScope(MgmtPermissions.VIEW_SCOPE); + return viewScope; + } + + public Scope realmScope(String scope) { + ResourceServer server = realmResourceServer(); + if (server == null) return null; + return authz.getStoreFactory().getScopeStore().findByName(scope, server.getId()); + } + + public boolean evaluatePermission(Resource resource, Scope scope, ResourceServer resourceServer) { + Identity identity = identity(); + if (identity == null) { + throw new RuntimeException("Identity of admin is not set for permission query"); + } + RealmModel oldRealm = session.getContext().getRealm(); + try { + session.getContext().setRealm(realm); + EvaluationContext context = new DefaultEvaluationContext(identity, session); + DecisionResult decisionCollector = new DecisionResult(); + List permissions = Permissions.permission(resourceServer, resource, scope); + PermissionEvaluator from = authz.evaluators().from(permissions, context); + from.evaluate(decisionCollector); + if (!decisionCollector.completed()) { + logger.error("Failed to run permission check", decisionCollector.getError()); + return false; + } + return decisionCollector.getResults().get(0).getEffect() == Decision.Effect.PERMIT; + } finally { + session.getContext().setRealm(oldRealm); + } + } + + + +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmAuth.java similarity index 86% rename from services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java rename to services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmAuth.java index 176c480382..61f304111b 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmAuth.java @@ -15,24 +15,21 @@ * limitations under the License. */ -package org.keycloak.services.resources.admin; +package org.keycloak.services.resources.admin.permissions; import org.keycloak.models.AdminRoles; import org.keycloak.models.ClientModel; import org.keycloak.models.ImpersonationConstants; import org.keycloak.services.ForbiddenException; +import org.keycloak.services.resources.admin.AdminAuth; /** * @author Stian Thorgersen */ -public class RealmAuth { +class RealmAuth { - private Resource resource; - - public enum Resource { - CLIENT, USER, REALM, EVENTS, IDENTITY_PROVIDER, IMPERSONATION, AUTHORIZATION - } + private AdminAuth.Resource resource; private AdminAuth auth; private ClientModel realmAdminApp; @@ -42,7 +39,7 @@ public class RealmAuth { this.realmAdminApp = realmAdminApp; } - public RealmAuth init(Resource resource) { + public RealmAuth init(AdminAuth.Resource resource) { this.resource = resource; return this; } @@ -52,11 +49,15 @@ public class RealmAuth { } public void requireAny() { - if (!auth.hasOneOfAppRole(realmAdminApp, AdminRoles.ALL_REALM_ROLES)) { + if (!hasAny()) { throw new ForbiddenException(); } } + public boolean hasAny() { + return auth.hasOneOfAppRole(realmAdminApp, AdminRoles.ALL_REALM_ROLES); + } + public boolean hasView() { return auth.hasOneOfAppRole(realmAdminApp, getViewRole(resource), getManageRole(resource)); } @@ -77,7 +78,7 @@ public class RealmAuth { } } - private String getViewRole(Resource resource) { + private String getViewRole(AdminAuth.Resource resource) { switch (resource) { case CLIENT: return AdminRoles.VIEW_CLIENTS; @@ -96,7 +97,7 @@ public class RealmAuth { } } - private String getManageRole(Resource resource) { + private String getManageRole(AdminAuth.Resource resource) { switch (resource) { case CLIENT: return AdminRoles.MANAGE_CLIENTS; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissionEvaluator.java new file mode 100644 index 0000000000..cf350a1e31 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissionEvaluator.java @@ -0,0 +1,59 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface RealmPermissionEvaluator { + boolean canListRealm(); + + void requireViewRealmNameList(); + + boolean canManageRealm(); + + void requireManageRealm(); + + boolean canViewRealm(); + + void requireViewRealm(); + + boolean canManageIdentityProviders(); + + boolean canViewIdentityProviders(); + + void requireViewIdentityProviders(); + + void requireManageIdentityProviders(); + + boolean canManageAuthorization(); + + boolean canViewAuthorization(); + + void requireManageAuthorization(); + + void requireViewAuthorization(); + + boolean canManageEvents(); + + void requireManageEvents(); + + boolean canViewEvents(); + + void requireViewEvents(); +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissions.java new file mode 100644 index 0000000000..84b6ceb6eb --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissions.java @@ -0,0 +1,188 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.jboss.logging.Logger; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.models.AdminRoles; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.services.ForbiddenException; + +/** + * Manages default policies for all users. + * + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +class RealmPermissions implements RealmPermissionEvaluator { + private static final Logger logger = Logger.getLogger(RealmPermissions.class); + protected final KeycloakSession session; + protected final RealmModel realm; + protected final AuthorizationProvider authz; + protected final MgmtPermissions root; + + public RealmPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) { + this.session = session; + this.realm = realm; + this.authz = authz; + this.root = root; + } + + + public boolean canManageRealmDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_REALM); + + } + public boolean canViewRealmDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_REALM, AdminRoles.VIEW_REALM); + } + + public boolean canManageIdentityProvidersDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_IDENTITY_PROVIDERS); + + } + public boolean canViewIdentityProvidersDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_IDENTITY_PROVIDERS, AdminRoles.VIEW_IDENTITY_PROVIDERS); + } + + public boolean canManageAuthorizationDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_AUTHORIZATION); + + } + public boolean canViewAuthorizationDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_AUTHORIZATION, AdminRoles.VIEW_AUTHORIZATION); + } + public boolean canManageEventsDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_EVENTS); + } + public boolean canViewEventsDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_EVENTS, AdminRoles.VIEW_EVENTS); + } + + @Override + public boolean canListRealm() { + return root.hasAnyAdminRole(); + } + + @Override + public void requireViewRealmNameList() { + if (!canListRealm()) { + throw new ForbiddenException(); + } + } + + @Override + public boolean canManageRealm() { + return canManageRealmDefault(); + } + + @Override + public void requireManageRealm() { + if (!canManageRealm()) { + throw new ForbiddenException(); + } + } + @Override + public boolean canViewRealm() { + return canViewRealmDefault(); + } + + @Override + public void requireViewRealm() { + if (!canViewRealm()) { + throw new ForbiddenException(); + } + } + + @Override + public boolean canManageIdentityProviders() { + return canManageIdentityProvidersDefault(); + } + + @Override + public boolean canViewIdentityProviders() { + return canViewIdentityProvidersDefault(); + } + + @Override + public void requireViewIdentityProviders() { + if (!canViewIdentityProviders()) { + throw new ForbiddenException(); + } + } + + + @Override + public void requireManageIdentityProviders() { + if (!canManageIdentityProviders()) { + throw new ForbiddenException(); + } + } + + + @Override + public boolean canManageAuthorization() { + return canManageAuthorizationDefault(); + } + + @Override + public boolean canViewAuthorization() { + return canViewAuthorizationDefault(); + } + + @Override + public void requireManageAuthorization() { + if (!canManageEvents()) { + throw new ForbiddenException(); + } + } + @Override + public void requireViewAuthorization() { + if (!canManageEvents()) { + throw new ForbiddenException(); + } + } + + @Override + public boolean canManageEvents() { + return canManageEventsDefault(); + } + + @Override + public void requireManageEvents() { + if (!canManageEvents()) { + throw new ForbiddenException(); + } + } + @Override + public boolean canViewEvents() { + return canViewEventsDefault(); + } + + @Override + public void requireViewEvents() { + if (!canViewEvents()) { + throw new ForbiddenException(); + } + } + + + + +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java new file mode 100644 index 0000000000..3a87cb3d47 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.models.RoleContainerModel; +import org.keycloak.models.RoleModel; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface RolePermissionEvaluator { + boolean canList(RoleContainerModel container); + + void requireList(RoleContainerModel container); + + boolean canMapRole(RoleModel role); + void requireMapRole(RoleModel role); + + boolean canManage(RoleModel role); + + void requireManage(RoleModel role); + + boolean canView(RoleModel role); + + void requireView(RoleModel role); + + boolean canMapClientScope(RoleModel role); + void requireMapClientScope(RoleModel role); + + boolean canMapComposite(RoleModel role); + void requireMapComposite(RoleModel role); +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionManagement.java new file mode 100644 index 0000000000..977e109d3e --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionManagement.java @@ -0,0 +1,56 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.models.RoleContainerModel; +import org.keycloak.models.RoleModel; + +import java.util.Map; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface RolePermissionManagement { + public static final String MAP_ROLE_SCOPE = "map-role"; + public static final String MAP_ROLE_CLIENT_SCOPE_SCOPE = "map-role-client-scope"; + public static final String MAP_ROLE_COMPOSITE_SCOPE = "map-role-composite"; + + boolean isPermissionsEnabled(RoleModel role); + void setPermissionsEnabled(RoleModel role, boolean enable); + + Map getPermissions(RoleModel role); + + Policy mapRolePermission(RoleModel role); + + Policy mapCompositePermission(RoleModel role); + + Policy mapClientScopePermission(RoleModel role); + + Resource resource(RoleModel role); + + ResourceServer resourceServer(RoleModel role); + + Policy manageUsersPolicy(ResourceServer server); + + Policy viewUsersPolicy(ResourceServer server); + + Policy rolePolicy(ResourceServer server, RoleModel role); +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java new file mode 100644 index 0000000000..d2531ec02d --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java @@ -0,0 +1,378 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.jboss.logging.Logger; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.authorization.store.ResourceStore; +import org.keycloak.models.AdminRoles; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleContainerModel; +import org.keycloak.models.RoleModel; +import org.keycloak.services.ForbiddenException; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +class RolePermissions implements RolePermissionEvaluator, RolePermissionManagement { + private static final Logger logger = Logger.getLogger(RolePermissions.class); + protected final KeycloakSession session; + protected final RealmModel realm; + protected final AuthorizationProvider authz; + protected final MgmtPermissions root; + + public RolePermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) { + this.session = session; + this.realm = realm; + this.authz = authz; + this.root = root; + } + + @Override + public boolean isPermissionsEnabled(RoleModel role) { + return mapRolePermission(role) != null; + } + + @Override + public void setPermissionsEnabled(RoleModel role, boolean enable) { + if (enable) { + ResourceServer server = getResourceServer(role); + if (authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()) != null) { + return; + } + createResource(role); + } else { + ResourceServer server = resourceServer(role); + if (server == null) return; + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()); + if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId()); + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolePermissionName(role), server.getId()); + if (policy != null) authz.getStoreFactory().getPolicyStore().delete(policy.getId()); + } + } + + @Override + public Map getPermissions(RoleModel role) { + Map scopes = new HashMap<>(); + scopes.put(RolePermissionManagement.MAP_ROLE_SCOPE, mapRolePermission(role).getId()); + scopes.put(RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE, mapClientScopePermission(role).getId()); + scopes.put(RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE, mapCompositePermission(role).getId()); + return scopes; + } + + @Override + public Policy mapRolePermission(RoleModel role) { + ResourceServer server = resourceServer(role); + if (server == null) return null; + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()); + if (resource == null) return null; + + return authz.getStoreFactory().getPolicyStore().findByName(getMapRolePermissionName(role), server.getId()); + } + + @Override + public Policy mapCompositePermission(RoleModel role) { + ResourceServer server = resourceServer(role); + if (server == null) return null; + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()); + if (resource == null) return null; + + return authz.getStoreFactory().getPolicyStore().findByName(getMapCompositePermissionName(role), server.getId()); + } + + @Override + public Policy mapClientScopePermission(RoleModel role) { + ResourceServer server = resourceServer(role); + if (server == null) return null; + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()); + if (resource == null) return null; + + return authz.getStoreFactory().getPolicyStore().findByName(getMapClientScopePermissionName(role), server.getId()); + } + + @Override + public Resource resource(RoleModel role) { + ResourceStore resourceStore = authz.getStoreFactory().getResourceStore(); + ResourceServer server = resourceServer(role); + if (server == null) return null; + return resourceStore.findByName(getRoleResourceName(role), server.getId()); + } + + @Override + public ResourceServer resourceServer(RoleModel role) { + ClientModel client = getRoleClient(role); + return authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + } + + /** + * Is admin allowed to map this role? + * + * @param role + * @return + */ + @Override + public boolean canMapRole(RoleModel role) { + if (!root.isAdminSameRealm()) { + return root.users().canManage(); + } + if (!isPermissionsEnabled(role)){ + return root.users().canManage(); + } + + ResourceServer resourceServer = getResourceServer(role); + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolePermissionName(role), resourceServer.getId()); + if (policy.getAssociatedPolicies().isEmpty()) { + return root.users().canManage(); // if no policies applied, just do default + } + + Resource roleResource = resource(role); + Scope mapRoleScope = getMapRoleScope(resourceServer); + return root.evaluatePermission(roleResource, mapRoleScope, resourceServer); + } + + @Override + public void requireMapRole(RoleModel role) { + if (!canMapRole(role)) { + throw new ForbiddenException(); + } + + } + + @Override + public boolean canList(RoleContainerModel container) { + return root.hasAnyAdminRole(); + } + + @Override + public void requireList(RoleContainerModel container) { + if (!canList(container)) { + throw new ForbiddenException(); + } + + } + + @Override + public boolean canMapComposite(RoleModel role) { + if (!root.isAdminSameRealm()) { + return canManage(role); + } + if (!isPermissionsEnabled(role)){ + return canManage(role); + } + + ResourceServer resourceServer = getResourceServer(role); + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapCompositePermissionName(role), resourceServer.getId()); + if (policy.getAssociatedPolicies().isEmpty()) { + return canManage(role); + } + + Resource roleResource = resource(role); + Scope scope = getMapCompositeScope(resourceServer); + return root.evaluatePermission(roleResource, scope, resourceServer); + } + + @Override + public void requireMapComposite(RoleModel role) { + if (!canMapComposite(role)) { + throw new ForbiddenException(); + } + + } + + + @Override + public boolean canMapClientScope(RoleModel role) { + if (!root.isAdminSameRealm()) { + return root.clients().canManage(); + } + if (!isPermissionsEnabled(role)){ + return root.clients().canManage(); + } + + ResourceServer resourceServer = getResourceServer(role); + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapClientScopePermissionName(role), resourceServer.getId()); + if (policy.getAssociatedPolicies().isEmpty()) { + return root.clients().canManage(); + } + + Resource roleResource = resource(role); + Scope scope = getMapClientScope(resourceServer); + return root.evaluatePermission(roleResource, scope, resourceServer); + } + + @Override + public void requireMapClientScope(RoleModel role) { + if (!canMapClientScope(role)) { + throw new ForbiddenException(); + } + } + + + @Override + public boolean canManage(RoleModel role) { + if (role.getContainer() instanceof RealmModel) { + return root.realm().canManageRealm(); + } else if (role.getContainer() instanceof ClientModel) { + ClientModel client = (ClientModel)role.getContainer(); + return root.clients().canManage(client); + } + return false; + } + + @Override + public void requireManage(RoleModel role) { + if (!canManage(role)) { + throw new ForbiddenException(); + } + + } + + @Override + public boolean canView(RoleModel role) { + if (role.getContainer() instanceof RealmModel) { + return root.realm().canViewRealm(); + } else if (role.getContainer() instanceof ClientModel) { + ClientModel client = (ClientModel)role.getContainer(); + return root.clients().canView(client); + } + return false; + } + + @Override + public void requireView(RoleModel role) { + if (!canView(role)) { + throw new ForbiddenException(); + } + + } + + private ClientModel getRoleClient(RoleModel role) { + ClientModel client = null; + if (role.getContainer() instanceof ClientModel) { + client = (ClientModel)role.getContainer(); + } else { + client = root.getRealmManagementClient(); + } + return client; + } + + @Override + public Policy manageUsersPolicy(ResourceServer server) { + RoleModel role = root.getRealmManagementClient().getRole(AdminRoles.MANAGE_USERS); + return rolePolicy(server, role); + } + + @Override + public Policy viewUsersPolicy(ResourceServer server) { + RoleModel role = root.getRealmManagementClient().getRole(AdminRoles.VIEW_USERS); + return rolePolicy(server, role); + } + + @Override + public Policy rolePolicy(ResourceServer server, RoleModel role) { + String policyName = Helper.getRolePolicyName(role); + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(policyName, server.getId()); + if (policy != null) return policy; + return Helper.createRolePolicy(authz, server, role, policyName); + } + + private Scope getMapRoleScope(ResourceServer server) { + Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_SCOPE, server.getId()); + if (scope == null) { + scope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_SCOPE, server); + } + return scope; + } + + private Scope getMapClientScope(ResourceServer server) { + Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_CLIENT_SCOPE_SCOPE, server.getId()); + if (scope == null) { + scope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_CLIENT_SCOPE_SCOPE, server); + } + return scope; + } + + private Scope getMapCompositeScope(ResourceServer server) { + Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_COMPOSITE_SCOPE, server.getId()); + if (scope == null) { + scope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_COMPOSITE_SCOPE, server); + } + return scope; + } + + + private Resource createResource(RoleModel role) { + ResourceServer server = getResourceServer(role); + Resource resource = authz.getStoreFactory().getResourceStore().create(getRoleResourceName(role), server, server.getClientId()); + resource.setType("Role"); + Scope mapRoleScope = getMapRoleScope(server); + Policy policy = manageUsersPolicy(server); + Helper.addScopePermission(authz, server, getMapRolePermissionName(role), resource, mapRoleScope, policy); + + Scope mapClientScope = getMapClientScope(server); + RoleModel mngClients = root.getRealmManagementClient().getRole(AdminRoles.MANAGE_CLIENTS); + Policy mngClientsPolicy = rolePolicy(server, mngClients); + Helper.addScopePermission(authz, server, getMapClientScopePermissionName(role), resource, mapClientScope, mngClientsPolicy); + + Scope mapCompositeScope = getMapCompositeScope(server); + if (role.getContainer() instanceof RealmModel) { + RoleModel mngRealm = root.getRealmManagementClient().getRole(AdminRoles.MANAGE_REALM); + policy = rolePolicy(server, mngRealm); + } else { + policy = mngClientsPolicy; + + } + Helper.addScopePermission(authz, server, getMapCompositePermissionName(role), resource, mapCompositeScope, policy); + return resource; + } + + private String getMapRolePermissionName(RoleModel role) { + return MAP_ROLE_SCOPE + ".permission." + role.getName(); + } + + private String getMapClientScopePermissionName(RoleModel role) { + return MAP_ROLE_CLIENT_SCOPE_SCOPE + ".permission." + role.getName(); + } + + private String getMapCompositePermissionName(RoleModel role) { + return MAP_ROLE_CLIENT_SCOPE_SCOPE + ".permission." + role.getName(); + } + + private ResourceServer getResourceServer(RoleModel role) { + ClientModel client = getRoleClient(role); + return root.findOrCreateResourceServer(client); + } + + private static String getRoleResourceName(RoleModel role) { + return "role.resource." + role.getName(); + } + + +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java new file mode 100644 index 0000000000..488825b559 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.models.UserModel; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface UserPermissionEvaluator { + boolean canManage(); + + void requireManage(); + + boolean canManage(UserModel user); + void requireManage(UserModel user); + + boolean canQuery(); + + void requireQuery(); + + boolean canView(); + boolean canView(UserModel user); + void requireView(UserModel user); + + void requireView(); + + boolean canImpersonate(UserModel user); + + void requireImpersonate(UserModel user); +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java new file mode 100644 index 0000000000..b57b710d0d --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java @@ -0,0 +1,41 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.models.RoleModel; + +import java.util.Map; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface UserPermissionManagement { + boolean isPermissionsEnabled(); + + void setPermissionsEnabled(boolean enable); + + Map getPermissions(); + + Resource resource(); + + Policy managePermission(); + + Policy viewPermission(); +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java new file mode 100644 index 0000000000..a0a7281e27 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java @@ -0,0 +1,382 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.services.resources.admin.permissions; + +import org.jboss.logging.Logger; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.models.AdminRoles; +import org.keycloak.models.ClientModel; +import org.keycloak.models.GroupModel; +import org.keycloak.models.ImpersonationConstants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.ForbiddenException; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Manages default policies for all users. + * + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +class UserPermissions implements UserPermissionEvaluator, UserPermissionManagement { + private static final Logger logger = Logger.getLogger(UserPermissions.class); + public static final String MANAGE_PERMISSION_USERS = "manage.permission.users"; + public static final String VIEW_PERMISSION_USERS = "view.permission.users"; + public static final String USERS_RESOURCE = "Users"; + protected final KeycloakSession session; + protected final RealmModel realm; + protected final AuthorizationProvider authz; + protected final MgmtPermissions root; + + public UserPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) { + this.session = session; + this.realm = realm; + this.authz = authz; + this.root = root; + } + + + private void initialize() { + root.initializeRealmResourceServer(); + root.initializeRealmDefaultScopes(); + ResourceServer server = root.realmResourceServer(); + Scope manageScope = root.realmManageScope(); + Scope viewScope = root.realmViewScope(); + + Resource usersResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); + if (usersResource == null) { + usersResource = authz.getStoreFactory().getResourceStore().create(USERS_RESOURCE, server, server.getClientId()); + Set scopeset = new HashSet<>(); + scopeset.add(manageScope); + scopeset.add(viewScope); + usersResource.updateScopes(scopeset); + } + Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId()); + if (managePermission == null) { + Policy manageUsersPolicy = root.roles().manageUsersPolicy(server); + Helper.addScopePermission(authz, server, MANAGE_PERMISSION_USERS, usersResource, manageScope, manageUsersPolicy); + } + Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(VIEW_PERMISSION_USERS, server.getId()); + if (viewPermission == null) { + Policy viewUsersPolicy = root.roles().viewUsersPolicy(server); + Helper.addScopePermission(authz, server, VIEW_PERMISSION_USERS, usersResource, viewScope, viewUsersPolicy); + } + } + + @Override + public Map getPermissions() { + Map scopes = new HashMap<>(); + scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission().getId()); + scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission().getId()); + return scopes; + } + + @Override + public boolean isPermissionsEnabled() { + ResourceServer server = root.realmResourceServer(); + if (server == null) return false; + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); + if (resource == null) return false; + + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId()); + + return policy != null; + } + + @Override + public void setPermissionsEnabled(boolean enable) { + ClientModel client = root.getRealmManagementClient(); + if (enable) { + initialize(); + } else { + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) return; + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId()); + if (policy == null) { + authz.getStoreFactory().getPolicyStore().delete(policy.getId()); + + } + Resource usersResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); + if (usersResource == null) { + authz.getStoreFactory().getResourceStore().delete(usersResource.getId()); + } + } + } + + public boolean canManageDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_USERS); + } + + @Override + public Resource resource() { + ResourceServer server = root.realmResourceServer(); + if (server == null) return null; + + return authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); + } + + @Override + public Policy managePermission() { + ResourceServer server = root.realmResourceServer(); + return authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId()); + } + + @Override + public Policy viewPermission() { + ResourceServer server = root.realmResourceServer(); + return authz.getStoreFactory().getPolicyStore().findByName(VIEW_PERMISSION_USERS, server.getId()); + } + + + + /** + * Is admin allowed to manage all users? In Authz terms, does the admin have the "manage" scope for the Users Authz resource? + * + * This method will follow the old default behavior (does the admin have the manage-users role) if any of these conditions + * are met.: + * - The admin is from the master realm managing a different realm + * - If the Authz objects are not set up correctly for the Users resource in Authz + * - The "manage" permission for the Users resource has an empty associatedPolicy list. + * + * Otherwise, it will use the Authz policy engine to resolve this answer. + * + * @return + */ + @Override + public boolean canManage() { + if (!root.isAdminSameRealm()) { + return canManageDefault(); + } + + ResourceServer server = root.realmResourceServer(); + if (server == null) return canManageDefault(); + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); + if (resource == null) return canManageDefault(); + + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId()); + if (policy == null) { + return canManageDefault(); + } + + Set associatedPolicies = policy.getAssociatedPolicies(); + // if no policies attached to permission then just do default behavior + if (associatedPolicies == null || associatedPolicies.isEmpty()) { + return canManageDefault(); + } + + Scope scope = root.realmManageScope(); + return root.evaluatePermission(resource, scope, server); + + } + + @Override + public void requireManage() { + if (!canManage()) { + throw new ForbiddenException(); + } + } + + + /** + * Does current admin have manage permissions for this particular user? + * + * @param user + * @return + */ + @Override + public boolean canManage(UserModel user) { + return canManage() || canManageByGroup(user); + } + + @Override + public void requireManage(UserModel user) { + if (!canManage(user)) { + throw new ForbiddenException(); + } + } + + private interface EvaluateGroup { + boolean evaluate(GroupModel group); + } + + private boolean evaluateGroups(UserModel user, EvaluateGroup eval) { + for (GroupModel group : user.getGroups()) { + if (eval.evaluate(group)) return true; + } + return false; + } + + private boolean evaluateHierarchy(UserModel user, EvaluateGroup eval) { + Set visited = new HashSet<>(); + for (GroupModel group : user.getGroups()) { + if (evaluateHierarchy(eval, group, visited)) return true; + } + return false; + } + + private boolean evaluateHierarchy(EvaluateGroup eval, GroupModel group, Set visited) { + if (visited.contains(group)) return false; + if (eval.evaluate(group)) { + return true; + } + visited.add(group); + if (group.getParent() == null) return false; + return evaluateHierarchy(eval, group.getParent(), visited); + } + + private boolean canManageByGroup(UserModel user) { + /* no inheritance + return evaluateGroups(user, + (group) -> root.groups().canViewMembers(group) + ); + */ + + /* inheritance + */ + return evaluateHierarchy(user, (group) -> root.groups().canManageMembers(group)); + + } + private boolean canViewByGroup(UserModel user) { + /* no inheritance + return evaluateGroups(user, + (group) -> root.groups().canViewMembers(group) + ); + */ + + /* inheritance + */ + return evaluateHierarchy(user, (group) -> root.groups().canViewMembers(group)); + } + + public boolean canViewDefault() { + return root.hasOneAdminRole(AdminRoles.MANAGE_USERS, AdminRoles.VIEW_USERS); + } + + @Override + public boolean canQuery() { + return canViewDefault(); + } + + @Override + public void requireQuery() { + if (!canQuery()) { + throw new ForbiddenException(); + } + } + + + + /** + * Is admin allowed to view all users? In Authz terms, does the admin have the "view" scope for the Users Authz resource? + * + * This method will follow the old default behavior (does the admin have the view-users role) if any of these conditions + * are met.: + * - The admin is from the master realm managing a different realm + * - If the Authz objects are not set up correctly for the Users resource in Authz + * - The "view" permission for the Users resource has an empty associatedPolicy list. + * + * Otherwise, it will use the Authz policy engine to resolve this answer. + * + * @return + */ + @Override + public boolean canView() { + if (!root.isAdminSameRealm()) { + return canViewDefault(); + } + + ResourceServer server = root.realmResourceServer(); + if (server == null) return canViewDefault(); + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId()); + if (resource == null) return canViewDefault(); + + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(VIEW_PERMISSION_USERS, server.getId()); + if (policy == null) { + return canViewDefault(); + } + + Set associatedPolicies = policy.getAssociatedPolicies(); + // if no policies attached to permission then just do default behavior + if (associatedPolicies == null || associatedPolicies.isEmpty()) { + return canViewDefault(); + } + + Scope scope = root.realmViewScope(); + return root.evaluatePermission(resource, scope, server); + } + + /** + * Does current admin have view permissions for this particular user? + * + * Evaluates in this order. If any true, return true: + * - canViewUsers + * - canManageUsers + * + * + * @param user + * @return + */ + @Override + public boolean canView(UserModel user) { + return canView() || canManage() || canViewByGroup(user) || canManageByGroup(user); + } + + @Override + public void requireView(UserModel user) { + if (!canView(user)) { + throw new ForbiddenException(); + } + } + + @Override + public void requireView() { + if (!(canView() || canManage())) { + throw new ForbiddenException(); + } + } + + @Override + public boolean canImpersonate(UserModel user) { + return root.hasOneAdminRole(ImpersonationConstants.IMPERSONATION_ROLE); + } + + @Override + public void requireImpersonate(UserModel user) { + if (!canImpersonate(user)) { + throw new ForbiddenException(); + } + } + + + + + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java index e001f60f38..b6bdc4c11d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java @@ -19,8 +19,9 @@ package org.keycloak.testsuite.admin; import org.junit.Assert; import org.junit.Test; import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.resource.RealmResource; -import org.keycloak.authorization.admin.permissions.MgmtPermissions; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement; +import org.keycloak.services.resources.admin.permissions.AdminPermissions; import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.models.AdminRoles; @@ -35,24 +36,14 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; -import org.keycloak.representations.idm.authorization.DecisionEffect; import org.keycloak.representations.idm.authorization.DecisionStrategy; -import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest; -import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse; import org.keycloak.testsuite.AbstractKeycloakTest; -import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; import org.keycloak.testsuite.util.AdminClientUtil; import javax.ws.rs.ClientErrorException; -import javax.ws.rs.ForbiddenException; -import javax.ws.rs.core.Response; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Set; -import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN; -import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; import static org.keycloak.testsuite.auth.page.AuthRealm.TEST; /** @@ -73,7 +64,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { public static void setupPolices(KeycloakSession session) { RealmModel realm = session.realms().getRealmByName(TEST); - MgmtPermissions permissions = new MgmtPermissions(session, realm); + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); RoleModel realmRole = realm.addRole("realm-role"); RoleModel realmRole2 = realm.addRole("realm-role2"); ClientModel client1 = realm.addClient("role-namespace"); @@ -110,7 +101,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { // setup Users manage policies { permissions.users().setPermissionsEnabled(true); - ResourceServer server = permissions.users().resourceServer(); + ResourceServer server = permissions.realmResourceServer(); Policy managerPolicy = permissions.roles().rolePolicy(server, managerRole); Policy permission = permissions.users().managePermission(); permission.addAssociatedPolicy(managerPolicy); @@ -177,9 +168,8 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { // test authorized { UserModel user = session.users().getUserByUsername("authorized", realm); - MgmtPermissions permissionsForAdmin = new MgmtPermissions(session, realm); - permissionsForAdmin.setIdentity(user); - Assert.assertTrue(permissionsForAdmin.users().canManage(user)); + AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, user); + Assert.assertTrue(permissionsForAdmin.users().canManage()); Assert.assertTrue(permissionsForAdmin.roles().canMapRole(realmRole)); Assert.assertTrue(permissionsForAdmin.roles().canMapRole(realmRole2)); Assert.assertTrue(permissionsForAdmin.roles().canMapRole(clientRole)); @@ -187,9 +177,8 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { // test composite role { UserModel user = session.users().getUserByUsername("authorizedComposite", realm); - MgmtPermissions permissionsForAdmin = new MgmtPermissions(session, realm); - permissionsForAdmin.setIdentity(user); - Assert.assertTrue(permissionsForAdmin.users().canManage(user)); + AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, user); + Assert.assertTrue(permissionsForAdmin.users().canManage()); Assert.assertTrue(permissionsForAdmin.roles().canMapRole(realmRole)); Assert.assertTrue(permissionsForAdmin.roles().canMapRole(realmRole2)); Assert.assertTrue(permissionsForAdmin.roles().canMapRole(clientRole)); @@ -198,9 +187,8 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { // test unauthorized { UserModel user = session.users().getUserByUsername("unauthorized", realm); - MgmtPermissions permissionsForAdmin = new MgmtPermissions(session, realm); - permissionsForAdmin.setIdentity(user); - Assert.assertFalse(permissionsForAdmin.users().canManage(user)); + AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, user); + Assert.assertFalse(permissionsForAdmin.users().canManage()); Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole)); Assert.assertFalse(permissionsForAdmin.roles().canMapRole(clientRole)); @@ -210,9 +198,8 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { // test unauthorized mapper { UserModel user = session.users().getUserByUsername("unauthorizedMapper", realm); - MgmtPermissions permissionsForAdmin = new MgmtPermissions(session, realm); - permissionsForAdmin.setIdentity(user); - Assert.assertTrue(permissionsForAdmin.users().canManage(user)); + AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, user); + Assert.assertTrue(permissionsForAdmin.users().canManage()); Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole)); Assert.assertFalse(permissionsForAdmin.roles().canMapRole(clientRole)); // will result to true because realmRole2 does not have any policies attached to this permission @@ -391,6 +378,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { } // testRestEvaluationMasterRealm // testRestEvaluationMasterAdminTestRealm + // test role deletion that it cleans up authz objects } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java index 531157daa3..e502ffaa3a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java @@ -51,7 +51,7 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation; -import org.keycloak.services.resources.admin.RealmAuth.Resource; +import org.keycloak.services.resources.admin.AdminAuth.Resource; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; @@ -77,8 +77,8 @@ import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; -import static org.keycloak.services.resources.admin.RealmAuth.Resource.AUTHORIZATION; -import static org.keycloak.services.resources.admin.RealmAuth.Resource.CLIENT; +import static org.keycloak.services.resources.admin.AdminAuth.Resource.AUTHORIZATION; +import static org.keycloak.services.resources.admin.AdminAuth.Resource.CLIENT; import org.keycloak.testsuite.ProfileAssume; /** @@ -313,11 +313,18 @@ public class PermissionsTest extends AbstractKeycloakTest { realm.removeDefaultGroup("nosuch"); } }, Resource.REALM, true); + GroupRepresentation newGroup = new GroupRepresentation(); + newGroup.setName("sample"); + adminClient.realm(REALM_NAME).groups().add(newGroup); + GroupRepresentation group = adminClient.realms().realm(REALM_NAME).getGroupByPath("sample"); + invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.getGroupByPath("nosuch"); + realm.getGroupByPath("sample"); } - }, Resource.REALM, false); + }, Resource.USER, false); + + adminClient.realms().realm(REALM_NAME).groups().group(group.getId()).remove(); invoke(new InvocationWithResponse() { public void invoke(RealmResource realm, AtomicReference response) { @@ -954,6 +961,10 @@ public class PermissionsTest extends AbstractKeycloakTest { @Test public void roles() { + RoleRepresentation newRole = new RoleRepresentation(); + newRole.setName("sample-role"); + adminClient.realm(REALM_NAME).roles().create(newRole); + invoke(new Invocation() { public void invoke(RealmResource realm) { realm.roles().list(); @@ -961,12 +972,12 @@ public class PermissionsTest extends AbstractKeycloakTest { }, Resource.REALM, false, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.roles().get("nosuch").toRepresentation(); + realm.roles().get("sample-role").toRepresentation(); } }, Resource.REALM, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.roles().get("nosuch").update(new RoleRepresentation()); + realm.roles().get("sample-role").update(newRole); } }, Resource.REALM, true); invoke(new Invocation() { @@ -976,39 +987,42 @@ public class PermissionsTest extends AbstractKeycloakTest { }, Resource.REALM, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.roles().deleteRole("nosuch"); + realm.roles().deleteRole("sample-role"); + // need to recreate for other tests + realm.roles().create(newRole); } }, Resource.REALM, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.roles().get("nosuch").getRoleComposites(); + realm.roles().get("sample-role").getRoleComposites(); } }, Resource.REALM, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.roles().get("nosuch").addComposites(Collections.emptyList()); + realm.roles().get("sample-role").addComposites(Collections.emptyList()); } }, Resource.REALM, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.roles().get("nosuch").deleteComposites(Collections.emptyList()); + realm.roles().get("sample-role").deleteComposites(Collections.emptyList()); } }, Resource.REALM, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.roles().get("nosuch").getRoleComposites(); + realm.roles().get("sample-role").getRoleComposites(); } }, Resource.REALM, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.roles().get("nosuch").getRealmRoleComposites(); + realm.roles().get("sample-role").getRealmRoleComposites(); } }, Resource.REALM, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.roles().get("nosuch").getClientRoleComposites("nosuch"); + realm.roles().get("sample-role").getClientRoleComposites("nosuch"); } }, Resource.REALM, false); + adminClient.realms().realm(REALM_NAME).roles().deleteRole("sample-role"); } @Test @@ -1175,51 +1189,61 @@ public class PermissionsTest extends AbstractKeycloakTest { @Test public void rolesById() { + RoleRepresentation newRole = new RoleRepresentation(); + newRole.setName("role-by-id"); + adminClient.realm(REALM_NAME).roles().create(newRole); + RoleRepresentation role = adminClient.realm(REALM_NAME).roles().get("role-by-id").toRepresentation(); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.rolesById().getRole("nosuch"); + realm.rolesById().getRole(role.getId()); } }, Resource.REALM, false, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.rolesById().updateRole("nosuch", new RoleRepresentation()); + realm.rolesById().updateRole(role.getId(), role); } }, Resource.REALM, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.rolesById().deleteRole("nosuch"); + realm.rolesById().deleteRole(role.getId()); + // need to recreate for other tests + realm.roles().create(newRole); + RoleRepresentation temp = realm.roles().get("role-by-id").toRepresentation(); + role.setId(temp.getId()); } }, Resource.REALM, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.rolesById().getRoleComposites("nosuch"); + realm.rolesById().getRoleComposites(role.getId()); } }, Resource.REALM, false, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.rolesById().addComposites("nosuch", Collections.emptyList()); + realm.rolesById().addComposites(role.getId(), Collections.emptyList()); } }, Resource.REALM, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.rolesById().deleteComposites("nosuch", Collections.emptyList()); + realm.rolesById().deleteComposites(role.getId(), Collections.emptyList()); } }, Resource.REALM, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.rolesById().getRoleComposites("nosuch"); + realm.rolesById().getRoleComposites(role.getId()); } }, Resource.REALM, false, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.rolesById().getRealmRoleComposites("nosuch"); + realm.rolesById().getRealmRoleComposites(role.getId()); } }, Resource.REALM, false, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.rolesById().getClientRoleComposites("nosuch", "nosuch"); + realm.rolesById().getClientRoleComposites(role.getId(), "nosuch"); } }, Resource.REALM, false, true); + + adminClient.realm(REALM_NAME).roles().deleteRole("role-by-id"); } @Test @@ -1237,85 +1261,95 @@ public class PermissionsTest extends AbstractKeycloakTest { } }, Resource.USER, true); + GroupRepresentation group = adminClient.realms().realm(REALM_NAME).getGroupByPath("mygroup"); + ClientRepresentation realmAccessClient = adminClient.realms().realm(REALM_NAME).clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0); + + invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").toRepresentation(); + realm.groups().group(group.getId()).toRepresentation(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").update(new GroupRepresentation()); + realm.groups().group(group.getId()).update(group); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").remove(); - } - }, Resource.USER, true); - invoke(new Invocation() { - public void invoke(RealmResource realm) { - realm.groups().group("nosuch").members(0, 100); + realm.groups().group(group.getId()).members(0, 100); } }, Resource.USER, false); invoke(new InvocationWithResponse() { public void invoke(RealmResource realm, AtomicReference response) { - response.set(realm.groups().group("nosuch").subGroup(new GroupRepresentation())); + GroupRepresentation subgroup = new GroupRepresentation(); + subgroup.setName("sub"); + response.set(realm.groups().group(group.getId()).subGroup(subgroup)); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().getAll(); + realm.groups().group(group.getId()).roles().getAll(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().realmLevel().listAll(); + realm.groups().group(group.getId()).roles().realmLevel().listAll(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().realmLevel().listEffective(); + realm.groups().group(group.getId()).roles().realmLevel().listEffective(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().realmLevel().listAvailable(); + realm.groups().group(group.getId()).roles().realmLevel().listAvailable(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().realmLevel().add(Collections.emptyList()); + realm.groups().group(group.getId()).roles().realmLevel().add(Collections.emptyList()); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().realmLevel().remove(Collections.emptyList()); + realm.groups().group(group.getId()).roles().realmLevel().remove(Collections.emptyList()); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().clientLevel("nosuch").listAll(); + realm.groups().group(group.getId()).roles().clientLevel(realmAccessClient.getId()).listAll(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().clientLevel("nosuch").listEffective(); + realm.groups().group(group.getId()).roles().clientLevel(realmAccessClient.getId()).listEffective(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().clientLevel("nosuch").listAvailable(); + realm.groups().group(group.getId()).roles().clientLevel(realmAccessClient.getId()).listAvailable(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().clientLevel("nosuch").add(Collections.emptyList()); + realm.groups().group(group.getId()).roles().clientLevel(realmAccessClient.getId()).add(Collections.emptyList()); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.groups().group("nosuch").roles().clientLevel("nosuch").remove(Collections.emptyList()); + realm.groups().group(group.getId()).roles().clientLevel(realmAccessClient.getId()).remove(Collections.emptyList()); + } + }, Resource.USER, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + realm.groups().group(group.getId()).remove(); + group.setId(null); + realm.groups().add(group); + GroupRepresentation temp = realm.getGroupByPath("mygroup"); + group.setId(temp.getId()); } }, Resource.USER, true); } @@ -1323,26 +1357,30 @@ public class PermissionsTest extends AbstractKeycloakTest { // Permissions for impersonation tested in ImpersonationTest @Test public void users() { - invoke(new Invocation() { - public void invoke(RealmResource realm) { - realm.users().get("nosuch").toRepresentation(); - } - }, Resource.USER, false); invoke(new InvocationWithResponse() { public void invoke(RealmResource realm, AtomicReference response) { response.set(realm.users().create(UserBuilder.create().username("testuser").build())); } }, Resource.USER, true); + UserRepresentation user = adminClient.realms().realm(REALM_NAME).users().search("testUser").get(0); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").update(UserBuilder.create().enabled(true).build()); + realm.users().get(user.getId()).remove(); + realm.users().create(user); + UserRepresentation temp = realm.users().search("testUser").get(0); + user.setId(temp.getId()); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().search("foo", 0, 1); + realm.users().get(user.getId()).toRepresentation(); } }, Resource.USER, false); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + realm.users().get(user.getId()).update(user); + } + }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { realm.users().count(); @@ -1350,149 +1388,150 @@ public class PermissionsTest extends AbstractKeycloakTest { }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").getUserSessions(); + realm.users().get(user.getId()).getUserSessions(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").getOfflineSessions("nosuch"); + realm.users().get(user.getId()).getOfflineSessions("nosuch"); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").getFederatedIdentity(); + realm.users().get(user.getId()).getFederatedIdentity(); } }, Resource.USER, false); invoke(new InvocationWithResponse() { public void invoke(RealmResource realm, AtomicReference response) { response.set(realm.users() - .get("nosuch") + .get(user.getId()) .addFederatedIdentity("nosuch", FederatedIdentityBuilder.create().identityProvider("nosuch").userId("nosuch").userName("nosuch").build())); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").removeFederatedIdentity("nosuch"); + realm.users().get(user.getId()).removeFederatedIdentity("nosuch"); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").getConsents(); + realm.users().get(user.getId()).getConsents(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").revokeConsent("testclient"); + realm.users().get(user.getId()).revokeConsent("testclient"); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").logout(); + realm.users().get(user.getId()).logout(); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").remove(); + realm.users().get(user.getId()).resetPassword(CredentialBuilder.create().password("password").build()); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").resetPassword(CredentialBuilder.create().password("password").build()); + realm.users().get(user.getId()).removeTotp(); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").removeTotp(); + realm.users().get(user.getId()).resetPasswordEmail(); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").resetPasswordEmail(); + realm.users().get(user.getId()).executeActionsEmail(Collections.emptyList()); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").executeActionsEmail(Collections.emptyList()); + realm.users().get(user.getId()).sendVerifyEmail(); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").sendVerifyEmail(); - } - }, Resource.USER, true); - invoke(new Invocation() { - public void invoke(RealmResource realm) { - realm.users().get("nosuch").groups(); + realm.users().get(user.getId()).groups(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").leaveGroup("nosuch"); + realm.users().get(user.getId()).leaveGroup("nosuch"); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").joinGroup("nosuch"); + realm.users().get(user.getId()).joinGroup("nosuch"); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().getAll(); + realm.users().get(user.getId()).roles().getAll(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().realmLevel().listAll(); + realm.users().get(user.getId()).roles().realmLevel().listAll(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().realmLevel().listAvailable(); + realm.users().get(user.getId()).roles().realmLevel().listAvailable(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().realmLevel().listEffective(); + realm.users().get(user.getId()).roles().realmLevel().listEffective(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().realmLevel().add(Collections.emptyList()); + realm.users().get(user.getId()).roles().realmLevel().add(Collections.emptyList()); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().realmLevel().remove(Collections.emptyList()); + realm.users().get(user.getId()).roles().realmLevel().remove(Collections.emptyList()); } }, Resource.USER, true); + ClientRepresentation realmAccessClient = adminClient.realms().realm(REALM_NAME).clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().clientLevel("nosuch").listAll(); + realm.users().get(user.getId()).roles().clientLevel(realmAccessClient.getId()).listAll(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().clientLevel("nosuch").listAvailable(); + realm.users().get(user.getId()).roles().clientLevel(realmAccessClient.getId()).listAvailable(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().clientLevel("nosuch").listEffective(); + realm.users().get(user.getId()).roles().clientLevel(realmAccessClient.getId()).listEffective(); } }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().clientLevel("nosuch").add(Collections.emptyList()); + realm.users().get(user.getId()).roles().clientLevel(realmAccessClient.getId()).add(Collections.emptyList()); } }, Resource.USER, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.users().get("nosuch").roles().clientLevel("nosuch").remove(Collections.emptyList()); + realm.users().get(user.getId()).roles().clientLevel(realmAccessClient.getId()).remove(Collections.emptyList()); } }, Resource.USER, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + realm.users().search("foo", 0, 1); + } + }, Resource.USER, false); } @Test