diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java index 1aaaa2352c..07438d07f4 100644 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java @@ -25,6 +25,7 @@ import javax.ws.rs.POST; 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.MediaType; import javax.ws.rs.core.Response; import java.util.List; @@ -42,6 +43,17 @@ public interface ResourcesResource { @Path("{id}") ResourceResource resource(@PathParam("id") String id); + @GET + @NoCache + @Produces(MediaType.APPLICATION_JSON) + List find(@QueryParam("name") String name, + @QueryParam("uri") String uri, + @QueryParam("owner") String owner, + @QueryParam("type") String type, + @QueryParam("scope") String scope, + @QueryParam("first") Integer firstResult, + @QueryParam("max") Integer maxResult); + @GET @NoCache @Produces(MediaType.APPLICATION_JSON) 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 4af34adf60..884f2e1abc 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java @@ -47,6 +47,7 @@ import org.keycloak.protocol.ProtocolMapper; import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper; import org.keycloak.representations.AccessToken; import org.keycloak.services.Urls; +import org.keycloak.services.resources.admin.RealmAuth; import javax.ws.rs.Consumes; import javax.ws.rs.POST; @@ -74,20 +75,23 @@ import static java.util.Arrays.asList; public class PolicyEvaluationService { private final AuthorizationProvider authorization; + private final RealmAuth auth; @Context private HttpRequest httpRequest; private final ResourceServer resourceServer; - PolicyEvaluationService(ResourceServer resourceServer, AuthorizationProvider authorization) { + PolicyEvaluationService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { this.resourceServer = resourceServer; this.authorization = authorization; + this.auth = auth; } @POST @Consumes("application/json") @Produces("application/json") public void evaluate(PolicyEvaluationRequest evaluationRequest, @Suspended AsyncResponse asyncResponse) { + this.auth.requireView(); KeycloakIdentity identity = createIdentity(evaluationRequest); EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity); authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(authorization, identity, asyncResponse)); 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 3b9c1942c5..b179378d70 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java @@ -273,7 +273,7 @@ public class PolicyService { @Path("evaluate") public PolicyEvaluationService getPolicyEvaluateResource() { this.auth.requireView(); - PolicyEvaluationService resource = new PolicyEvaluationService(this.resourceServer, this.authorization); + 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/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java index d31146f7d4..64ef0619c1 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java @@ -177,7 +177,7 @@ public class ResourceSetService { @GET @NoCache @Produces("application/json") - public Response findAll(@QueryParam("name") String name, + public Response find(@QueryParam("name") String name, @QueryParam("uri") String uri, @QueryParam("owner") String owner, @QueryParam("type") String type, 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 44464b4fe9..f050f324f1 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java @@ -107,6 +107,11 @@ public class ScopeService { } Scope scope = storeFactory.getScopeStore().findById(id); + + if (scope == null) { + return Response.status(Status.NOT_FOUND).build(); + } + PolicyStore policyStore = storeFactory.getPolicyStore(); List policies = policyStore.findByScopeIds(Arrays.asList(scope.getId()), resourceServer.getId()); diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java index b02a935238..fdaa12f951 100644 --- a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java +++ b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java @@ -109,7 +109,7 @@ public class ResourceService { } private Set findAll() { - Response response = this.resourceManager.findAll(null, null, null, null, null, -1, -1); + Response response = this.resourceManager.find(null, null, null, null, null, -1, -1); List resources = (List) response.getEntity(); return resources.stream().map(ResourceRepresentation::getId).collect(Collectors.toSet()); } 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 81de89463d..b6fca90ed3 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 @@ -17,10 +17,12 @@ package org.keycloak.testsuite.admin; +import org.apache.bcel.generic.RETURN; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput; import org.junit.Rule; import org.junit.Test; import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.AuthorizationResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.models.AdminRoles; import org.keycloak.models.Constants; @@ -45,6 +47,10 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserFederationMapperRepresentation; import org.keycloak.representations.idm.UserFederationProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +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.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.Assert; @@ -68,6 +74,8 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.*; +import static org.keycloak.services.resources.admin.RealmAuth.Resource.AUTHORIZATION; +import static org.keycloak.services.resources.admin.RealmAuth.Resource.CLIENT; /** * @author Stian Thorgersen @@ -770,6 +778,123 @@ public class PermissionsTest extends AbstractKeycloakTest { }, Resource.CLIENT, true); } + @Test + public void clientAuthorization() { + invoke(new InvocationWithResponse() { + public void invoke(RealmResource realm, AtomicReference response) { + realm.clients().create(ClientBuilder.create().clientId("foo-authz").build()); + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + foo.setServiceAccountsEnabled(true); + foo.setAuthorizationServicesEnabled(true); + realm.clients().get(foo.getId()).update(foo); + } + }, CLIENT, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + realm.clients().get(foo.getId()).authorization().getSettings(); + } + }, AUTHORIZATION, false); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + ResourceServerRepresentation settings = authorization.getSettings(); + authorization.update(settings); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.resources().resources(); + } + }, AUTHORIZATION, false); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.scopes().scopes(); + } + }, AUTHORIZATION, false); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.policies().policies(); + } + }, AUTHORIZATION, false); + invoke(new InvocationWithResponse() { + public void invoke(RealmResource realm, AtomicReference response) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + response.set(authorization.resources().create(new ResourceRepresentation("Test", Collections.emptySet()))); + } + }, AUTHORIZATION, true); + invoke(new InvocationWithResponse() { + public void invoke(RealmResource realm, AtomicReference response) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + response.set(authorization.scopes().create(new ScopeRepresentation("Test"))); + } + }, AUTHORIZATION, true); + invoke(new InvocationWithResponse() { + public void invoke(RealmResource realm, AtomicReference response) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + PolicyRepresentation representation = new PolicyRepresentation(); + representation.setName("Test PermissionsTest"); + representation.setType("js"); + HashMap config = new HashMap<>(); + config.put("code", ""); + representation.setConfig(config); + response.set(authorization.policies().create(representation)); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.resources().resource("nosuch").update(new ResourceRepresentation()); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.scopes().scope("nosuch").update(new ScopeRepresentation()); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.policies().policy("nosuch").update(new PolicyRepresentation()); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.resources().resource("nosuch").remove(); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.scopes().scope("nosuch").remove(); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.policies().policy("nosuch").remove(); + } + }, AUTHORIZATION, true); + } + @Test public void roles() { invoke(new Invocation() { @@ -1543,6 +1668,8 @@ public class PermissionsTest extends AbstractKeycloakTest { return AdminRoles.VIEW_EVENTS; case IDENTITY_PROVIDER: return AdminRoles.VIEW_IDENTITY_PROVIDERS; + case AUTHORIZATION: + return AdminRoles.VIEW_AUTHORIZATION; default: throw new RuntimeException("Unexpected resouce"); } @@ -1560,6 +1687,8 @@ public class PermissionsTest extends AbstractKeycloakTest { return AdminRoles.MANAGE_EVENTS; case IDENTITY_PROVIDER: return AdminRoles.MANAGE_IDENTITY_PROVIDERS; + case AUTHORIZATION: + return AdminRoles.MANAGE_AUTHORIZATION; default: throw new RuntimeException("Unexpected resouce"); } @@ -1577,6 +1706,8 @@ public class PermissionsTest extends AbstractKeycloakTest { return AdminRoles.VIEW_IDENTITY_PROVIDERS; case IDENTITY_PROVIDER: return AdminRoles.VIEW_REALM; + case AUTHORIZATION: + return AdminRoles.VIEW_IDENTITY_PROVIDERS; default: throw new RuntimeException("Unexpected resouce"); } @@ -1594,6 +1725,8 @@ public class PermissionsTest extends AbstractKeycloakTest { return AdminRoles.MANAGE_IDENTITY_PROVIDERS; case IDENTITY_PROVIDER: return AdminRoles.MANAGE_REALM; + case AUTHORIZATION: + return AdminRoles.MANAGE_IDENTITY_PROVIDERS; default: throw new RuntimeException("Unexpected resouce"); }