diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AbstractAuthorizationTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AbstractAuthorizationTest.java
new file mode 100644
index 0000000..fb7fecf
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AbstractAuthorizationTest.java
@@ -0,0 +1,144 @@
+/*
+ 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.admin.client.resource.ResourceScopeResource;
+import org.keycloak.admin.client.resource.ResourceScopesResource;
+import org.keycloak.client.testsuite.authz.AbstractAuthzTest;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.testsuite.util.ApiUtil;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.RoleBuilder;
+import org.keycloak.testsuite.util.RolesBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author Pedro Igor
+ */
+public abstract class AbstractAuthorizationTest extends AbstractAuthzTest {
+
+ protected static final String RESOURCE_SERVER_CLIENT_ID = "resource-server-test";
+
+ @Override
+ public List getRealmsForImport() {
+ List testRealms = new ArrayList<>();
+ testRealms.add(createTestRealm().build());
+ return testRealms;
+ }
+
+ @AfterEach
+ public void onAfterReenableAuthorization() {
+ enableAuthorizationServices(false);
+ enableAuthorizationServices(true);
+ }
+
+ protected RealmResource testRealmResource() {
+ return adminClient.realm("authz-test");
+ }
+
+ protected String getRealmId() {
+ return "authz-test";
+ }
+
+ protected ClientResource getClientResource() {
+ return ApiUtil.findClientResourceByName(testRealmResource(), RESOURCE_SERVER_CLIENT_ID);
+ }
+
+ protected ClientRepresentation getResourceServer() {
+ return findClientRepresentation(RESOURCE_SERVER_CLIENT_ID);
+ }
+
+ protected ClientRepresentation findClientRepresentation(String name) {
+ ClientResource clientRsc = findClientResource(name);
+ if (clientRsc == null) return null;
+ return findClientResource(name).toRepresentation();
+ }
+
+ protected ClientResource findClientResource(String name) {
+ return ApiUtil.findClientResourceByName(testRealmResource(), name);
+ }
+
+ protected ClientResource findClientResourceById(String id) {
+ return ApiUtil.findClientResourceByClientId(testRealmResource(), id);
+ }
+
+ protected void enableAuthorizationServices(boolean enable) {
+ ClientRepresentation resourceServer = getResourceServer();
+
+ resourceServer.setAuthorizationServicesEnabled(enable);
+ resourceServer.setServiceAccountsEnabled(true);
+ resourceServer.setPublicClient(false);
+ resourceServer.setSecret("secret");
+
+ getClientResource().update(resourceServer);
+
+ if (enable) {
+ AuthorizationResource authorization = getClientResource().authorization();
+ ResourceServerRepresentation settings = authorization.exportSettings();
+ settings.setAllowRemoteResourceManagement(true);
+ authorization.update(settings);
+ }
+ }
+
+ protected ResourceScopeResource createDefaultScope() {
+ return createScope("Test Scope", "Scope Icon");
+ }
+
+ protected ResourceScopeResource createScope(String name, String iconUri) {
+ ScopeRepresentation newScope = new ScopeRepresentation();
+
+ newScope.setName(name);
+ newScope.setIconUri(iconUri);
+
+ ResourceScopesResource resources = getClientResource().authorization().scopes();
+
+ try (Response response = resources.create(newScope)) {
+ Assertions.assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+
+ ScopeRepresentation stored = response.readEntity(ScopeRepresentation.class);
+
+ return resources.scope(stored.getId());
+ }
+ }
+
+ protected RealmBuilder createTestRealm() {
+ return RealmBuilder.create().name("authz-test")
+ .user(UserBuilder.create().username("marta").password("password"))
+ .user(UserBuilder.create().username("kolo").password("password"))
+ .roles(RolesBuilder.create().realmRole(RoleBuilder.create().name("realm-role").build()))
+ .client(ClientBuilder.create().clientId(RESOURCE_SERVER_CLIENT_ID)
+ .name(RESOURCE_SERVER_CLIENT_ID)
+ .secret("secret")
+ .authorizationServicesEnabled(true)
+ .redirectUris("http://localhost/" + RESOURCE_SERVER_CLIENT_ID)
+ .defaultRoles("uma_protection")
+ .directAccessGrants());
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AbstractPolicyManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AbstractPolicyManagementTest.java
new file mode 100644
index 0000000..dfc2307
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AbstractPolicyManagementTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Supplier;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.client.testsuite.authz.AbstractAuthzTest;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author Pedro Igor
+ */
+public abstract class AbstractPolicyManagementTest extends AbstractAuthzTest {
+
+ @Override
+ public List getRealmsForImport() {
+ List testRealms = new ArrayList<>();
+ testRealms.add(createTestRealm().build());
+ return testRealms;
+ }
+
+ protected RealmBuilder createTestRealm() {
+ return RealmBuilder.create().name("authz-test")
+ .user(UserBuilder.create().username("marta").password("password"))
+ .user(UserBuilder.create().username("kolo").password("password"))
+ .client(ClientBuilder.create().clientId("resource-server-test")
+ .secret("secret")
+ .authorizationServicesEnabled(true)
+ .redirectUris("http://localhost/resource-server-test")
+ .defaultRoles("uma_protection")
+ .directAccessGrants());
+ }
+
+ @BeforeEach
+ public void configureAuthorization() throws Exception {
+ createResourcesAndScopes();
+ RealmResource realm = getRealm();
+ createPolicies(realm, getClient(realm));
+ }
+
+ protected void assertRepresentation(AbstractPolicyRepresentation expected, AbstractPolicyRepresentation actual,
+ Supplier> resources,
+ Supplier> scopes,
+ Supplier> policies) {
+ Assertions.assertNotNull(actual);
+ Assertions.assertNotNull(actual.getId());
+
+ Assertions.assertEquals(expected.getName(), actual.getName());
+ Assertions.assertEquals(expected.getDescription(), actual.getDescription());
+ Assertions.assertEquals(expected.getDecisionStrategy(), actual.getDecisionStrategy());
+ Assertions.assertEquals(expected.getLogic(), actual.getLogic());
+ Assertions.assertNull(actual.getResources());
+ Assertions.assertNull(actual.getPolicies());
+ Assertions.assertNull(actual.getScopes());
+
+ List associatedPolicies = policies.get();
+
+ if (expected.getPolicies() != null) {
+ Assertions.assertEquals(expected.getPolicies().size(), associatedPolicies.size());
+ Assertions.assertEquals(0, associatedPolicies.stream()
+ .map(representation1 -> representation1.getName())
+ .filter(policyName -> !expected.getPolicies().contains(policyName))
+ .count());
+ } else {
+ Assertions.assertTrue(associatedPolicies.isEmpty());
+ }
+
+ List associatedResources = resources.get();
+
+ if (expected.getResources() != null) {
+ Assertions.assertEquals(expected.getResources().size(), associatedResources.size());
+ Assertions.assertEquals(0, associatedResources.stream()
+ .map(representation1 -> representation1.getName())
+ .filter(resourceName -> !expected.getResources().contains(resourceName))
+ .count());
+ } else {
+ Assertions.assertTrue(associatedResources.isEmpty());
+ }
+
+ List associatedScopes = scopes.get();
+
+ if (expected.getScopes() != null) {
+ Assertions.assertEquals(expected.getScopes().size(), associatedScopes.size());
+ Assertions.assertEquals(0, associatedScopes.stream()
+ .map(representation1 -> representation1.getName())
+ .filter(scopeName -> !expected.getScopes().contains(scopeName))
+ .count());
+ } else {
+ Assertions.assertTrue(associatedScopes.isEmpty());
+ }
+
+ expected.setId(actual.getId());
+ }
+
+ private void createResourcesAndScopes() throws IOException {
+ Set scopes = new HashSet<>();
+
+ scopes.add(new ScopeRepresentation("read"));
+ scopes.add(new ScopeRepresentation("write"));
+ scopes.add(new ScopeRepresentation("execute"));
+
+ List resources = new ArrayList<>();
+
+ resources.add(new ResourceRepresentation("Resource A", scopes));
+ resources.add(new ResourceRepresentation("Resource B", scopes));
+ resources.add(new ResourceRepresentation("Resource C", scopes));
+
+ resources.forEach(resource -> {
+ Response response = getClient().authorization().resources().create(resource);
+ response.close();
+ });
+ }
+
+ private void createPolicies(RealmResource realm, ClientResource client) throws IOException {
+ createUserPolicy("Only Marta Policy", realm, client, "marta");
+ createUserPolicy("Only Kolo Policy", realm, client, "kolo");
+ }
+
+ private void createUserPolicy(String name, RealmResource realm, ClientResource client, String username) throws IOException {
+ String userId = realm.users().search(username).stream().map(representation -> representation.getId()).findFirst().orElseThrow(() -> new RuntimeException("Expected user [userId]"));
+
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName(name);
+ representation.addUser(userId);
+
+ Response response = client.authorization().policies().user().create(representation);
+ response.close();
+ }
+
+ protected ClientResource getClient() {
+ return getClient(getRealm());
+ }
+
+ protected ClientResource getClient(RealmResource realm) {
+ ClientsResource clients = realm.clients();
+ return clients.findByClientId("resource-server-test").stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
+ }
+
+ protected RealmResource getRealm() {
+ try {
+ return adminClient.realm("authz-test");
+ } catch (Exception cause) {
+ throw new RuntimeException("Failed to create admin client", cause);
+ }
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AggregatePolicyManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AggregatePolicyManagementTest.java
new file mode 100644
index 0000000..f495f79
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AggregatePolicyManagementTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.Collections;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AggregatePoliciesResource;
+import org.keycloak.admin.client.resource.AggregatePolicyResource;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author Pedro Igor
+ */
+public class AggregatePolicyManagementTest extends AbstractPolicyManagementTest {
+
+ @Override
+ protected RealmBuilder createTestRealm() {
+ return super.createTestRealm()
+ .user(UserBuilder.create().username("AggregatePolicyManagementTestUser"));
+ }
+
+ @Test
+ public void testCreate() {
+ AuthorizationResource authorization = getClient().authorization();
+ AggregatePolicyRepresentation representation = new AggregatePolicyRepresentation();
+
+ representation.setName("Aggregate Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ AggregatePolicyRepresentation representation = new AggregatePolicyRepresentation();
+
+ representation.setName("Update Aggregate Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.getPolicies().clear();
+ representation.addPolicy("Only Kolo Policy");
+
+ AggregatePoliciesResource policies = authorization.policies().aggregate();
+ AggregatePolicyResource policy = policies.findById(representation.getId());
+
+ policy.update(representation);
+ assertRepresentation(representation, policy);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ AggregatePolicyRepresentation representation = new AggregatePolicyRepresentation();
+
+ representation.setName("Test Delete Policy");
+ representation.addPolicy("Only Marta Policy");
+
+ AggregatePoliciesResource policies = authorization.policies().aggregate();
+
+ try (Response response = policies.create(representation)) {
+ AggregatePolicyRepresentation created = response.readEntity(AggregatePolicyRepresentation.class);
+
+ policies.findById(created.getId()).remove();
+
+ AggregatePolicyResource removed = policies.findById(created.getId());
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class, () -> removed.toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+ }
+
+ //Issue #24651
+ @Test
+ public void testDeleteUser() {
+ AuthorizationResource authorization = getClient().authorization();
+
+ UsersResource users = getRealm().users();
+ UserRepresentation user = users.search("AggregatePolicyManagementTestUser").get(0);
+
+ UserPolicyRepresentation userPolicyRepresentation = new UserPolicyRepresentation();
+ userPolicyRepresentation.setName("AggregatePolicyManagementTestUser Only");
+ userPolicyRepresentation.setDescription("description");
+ userPolicyRepresentation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ userPolicyRepresentation.setLogic(Logic.NEGATIVE);
+ userPolicyRepresentation.addUser(user.getId());
+ authorization.policies().user().create(userPolicyRepresentation);
+
+ TimePolicyRepresentation timePolicyRepresentation = new TimePolicyRepresentation();
+ timePolicyRepresentation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ timePolicyRepresentation.setLogic(Logic.NEGATIVE);
+ timePolicyRepresentation.setName("Dayshift");
+ timePolicyRepresentation.setHour("8");
+ timePolicyRepresentation.setHourEnd("17");
+ authorization.policies().time().create(timePolicyRepresentation);
+
+ AggregatePolicyRepresentation representation = new AggregatePolicyRepresentation();
+ representation.setName("AggregatePolicyManagementTestUser Only during dayshift");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addPolicy("AggregatePolicyManagementTestUser Only", "Dayshift");
+ assertCreated(authorization, representation);
+
+ users.get(user.getId()).remove();
+
+ UserPolicyRepresentation actualUserPolicy = authorization.policies().user().findByName(userPolicyRepresentation.getName());
+ Assertions.assertEquals(0, actualUserPolicy.getUsers().size());
+
+ AggregatePolicyResource actual = authorization.policies().aggregate().findById(representation.getId());
+ assertRepresentation(representation, actual);
+ }
+
+ private void assertCreated(AuthorizationResource authorization, AggregatePolicyRepresentation representation) {
+ AggregatePoliciesResource permissions = authorization.policies().aggregate();
+ try (Response response = permissions.create(representation)) {
+ AggregatePolicyRepresentation created = response.readEntity(AggregatePolicyRepresentation.class);
+ AggregatePolicyResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+ }
+
+ private void assertRepresentation(AggregatePolicyRepresentation representation, AggregatePolicyResource policy) {
+ AggregatePolicyRepresentation actual = policy.toRepresentation();
+ assertRepresentation(representation, actual, () -> policy.resources(), () -> Collections.emptyList(), () -> policy.associatedPolicies());
+ }
+}
\ No newline at end of file
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AuthorizationTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AuthorizationTest.java
new file mode 100644
index 0000000..8fb13c9
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/AuthorizationTest.java
@@ -0,0 +1,124 @@
+/*
+ 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.client.testsuite.authz.admin;
+
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.common.constants.ServiceAccountConstants;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
+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.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+
+/**
+ *
+ * @author Pedro Igor
+ */
+public class AuthorizationTest extends AbstractAuthorizationTest {
+
+ @Test
+ public void testEnableAuthorizationServices() {
+ ClientResource clientResource = getClientResource();
+ ClientRepresentation resourceServer = getResourceServer();
+ RealmResource realm = testRealmResource();
+
+ UserRepresentation serviceAccount = realm.users().search(ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + resourceServer.getClientId()).get(0);
+ Assertions.assertNotNull(serviceAccount);
+ List serviceAccountRoles = realm.users().get(serviceAccount.getId()).roles().clientLevel(resourceServer.getId()).listEffective();
+ Assertions.assertTrue(serviceAccountRoles.stream().anyMatch(roleRepresentation -> "uma_protection".equals(roleRepresentation.getName())));
+
+ enableAuthorizationServices(false);
+ enableAuthorizationServices(true);
+
+ serviceAccount = clientResource.getServiceAccountUser();
+ Assertions.assertNotNull(serviceAccount);
+ serviceAccountRoles = realm.users().get(serviceAccount.getId()).roles().clientLevel(resourceServer.getId()).listEffective();
+ Assertions.assertTrue(serviceAccountRoles.stream().anyMatch(roleRepresentation -> "uma_protection".equals(roleRepresentation.getName())));
+
+ RolePolicyRepresentation policy = new RolePolicyRepresentation();
+
+ policy.setName("should be removed");
+ policy.addRole("uma_authorization");
+
+ clientResource.authorization().policies().role().create(policy);
+
+ List defaultResources = clientResource.authorization().resources().resources();
+
+ Assertions.assertEquals(1, defaultResources.size());
+
+ List defaultPolicies = clientResource.authorization().policies().policies();
+
+ Assertions.assertEquals(3, defaultPolicies.size());
+
+ enableAuthorizationServices(false);
+ enableAuthorizationServices(true);
+
+ ResourceServerRepresentation settings = clientResource.authorization().getSettings();
+
+ Assertions.assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING.name(), settings.getPolicyEnforcementMode().name());
+ Assertions.assertTrue(settings.isAllowRemoteResourceManagement());
+ Assertions.assertEquals(resourceServer.getId(), settings.getClientId());
+ defaultResources = clientResource.authorization().resources().resources();
+
+ Assertions.assertEquals(1, defaultResources.size());
+
+ defaultPolicies = clientResource.authorization().policies().policies();
+
+ Assertions.assertEquals(2, defaultPolicies.size());
+
+ serviceAccount = clientResource.getServiceAccountUser();
+ Assertions.assertNotNull(serviceAccount);
+ serviceAccountRoles = realm.users().get(serviceAccount.getId()).roles().clientLevel(resourceServer.getId()).listEffective();
+ Assertions.assertTrue(serviceAccountRoles.stream().anyMatch(roleRepresentation -> "uma_protection".equals(roleRepresentation.getName())));
+ }
+
+ // KEYCLOAK-6321
+ @Test
+ public void testRemoveDefaultResourceWithAdminEventsEnabled() {
+ RealmResource realmResource = testRealmResource();
+ RealmRepresentation realmRepresentation = realmResource.toRepresentation();
+
+ realmRepresentation.setAdminEventsEnabled(true);
+
+ realmResource.update(realmRepresentation);
+
+ ClientResource clientResource = getClientResource();
+ ClientRepresentation resourceServer = getResourceServer();
+
+ ResourceServerRepresentation settings = clientResource.authorization().getSettings();
+
+ Assertions.assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING.name(), settings.getPolicyEnforcementMode().name());
+ Assertions.assertEquals(resourceServer.getId(), settings.getClientId());
+ List defaultResources = clientResource.authorization().resources().resources();
+
+ Assertions.assertEquals(1, defaultResources.size());
+
+ clientResource.authorization().resources().resource(defaultResources.get(0).getId()).remove();
+
+ Assertions.assertTrue(clientResource.authorization().resources().resources().isEmpty());
+ }
+}
\ No newline at end of file
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ClientPolicyManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ClientPolicyManagementTest.java
new file mode 100644
index 0000000..30977a8
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ClientPolicyManagementTest.java
@@ -0,0 +1,216 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.Collections;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientPoliciesResource;
+import org.keycloak.admin.client.resource.ClientPolicyResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.PolicyResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.RealmBuilder;
+
+/**
+ * @author Pedro Igor
+ */
+public class ClientPolicyManagementTest extends AbstractPolicyManagementTest {
+
+ @Override
+ protected RealmBuilder createTestRealm() {
+ return super.createTestRealm()
+ .client(ClientBuilder.create().clientId("Client A"))
+ .client(ClientBuilder.create().clientId("Client B"))
+ .client(ClientBuilder.create().clientId("Client C"))
+ .client(ClientBuilder.create().clientId("Client D"))
+ .client(ClientBuilder.create().clientId("Client E"))
+ .client(ClientBuilder.create().clientId("Client F"));
+ }
+
+ @Test
+ public void testCreate() {
+ AuthorizationResource authorization = getClient().authorization();
+ ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+ representation.setName("Realm Client Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addClient("Client A");
+ representation.addClient("Client B");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+ representation.setName("Update Test Client Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addClient("Client A");
+ representation.addClient("Client B");
+ representation.addClient("Client C");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.setClients(representation.getClients().stream().filter(userName -> !userName.equals("Client A")).collect(Collectors.toSet()));
+
+ ClientPoliciesResource policies = authorization.policies().client();
+ ClientPolicyResource permission = policies.findById(representation.getId());
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+
+ representation.setClients(representation.getClients().stream().filter(userName -> !userName.equals("Client C")).collect(Collectors.toSet()));
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+ representation.setName("Test Delete Permission");
+ representation.addClient("Client A");
+
+ ClientPoliciesResource policies = authorization.policies().client();
+
+ try (Response response = policies.create(representation)) {
+ ClientPolicyRepresentation created = response.readEntity(ClientPolicyRepresentation.class);
+
+ policies.findById(created.getId()).remove();
+
+ ClientPolicyResource removed = policies.findById(created.getId());
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class, () -> removed.toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+ }
+
+
+ @Test
+ public void testDeleteClient() {
+ AuthorizationResource authorization = getClient().authorization();
+ ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+ representation.setName("Delete Test Client Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addClient("Client D");
+ representation.addClient("Client E");
+ representation.addClient("Client F");
+
+ assertCreated(authorization, representation);
+
+ ClientsResource clients = getRealm().clients();
+ ClientRepresentation client = clients.findByClientId("Client D").get(0);
+
+ clients.get(client.getId()).remove();
+
+ representation = authorization.policies().client().findById(representation.getId()).toRepresentation();
+
+ Assertions.assertEquals(2, representation.getClients().size());
+ Assertions.assertFalse(representation.getClients().contains(client.getId()));
+
+ client = clients.findByClientId("Client E").get(0);
+ clients.get(client.getId()).remove();
+
+ representation = authorization.policies().client().findById(representation.getId()).toRepresentation();
+
+ Assertions.assertEquals(1, representation.getClients().size());
+ Assertions.assertFalse(representation.getClients().contains(client.getId()));
+
+ client = clients.findByClientId("Client F").get(0);
+ clients.get(client.getId()).remove();
+
+ representation = authorization.policies().client().findById(representation.getId()).toRepresentation();
+
+ Assertions.assertEquals(0, representation.getClients().size());
+ Assertions.assertFalse(representation.getClients().contains(client.getId()));
+ }
+
+ @Test
+ public void testGenericConfig() {
+ AuthorizationResource authorization = getClient().authorization();
+ ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+ representation.setName("Test Generic Config Permission");
+ representation.addClient("Client A");
+
+ ClientPoliciesResource policies = authorization.policies().client();
+
+ try (Response response = policies.create(representation)) {
+ ClientPolicyRepresentation created = response.readEntity(ClientPolicyRepresentation.class);
+
+ PolicyResource policy = authorization.policies().policy(created.getId());
+ PolicyRepresentation genericConfig = policy.toRepresentation();
+
+ Assertions.assertNotNull(genericConfig.getConfig());
+ Assertions.assertNotNull(genericConfig.getConfig().get("clients"));
+
+ ClientRepresentation user = getRealm().clients().findByClientId("Client A").get(0);
+
+ Assertions.assertTrue(genericConfig.getConfig().get("clients").contains(user.getId()));
+ }
+ }
+
+ private void assertCreated(AuthorizationResource authorization, ClientPolicyRepresentation representation) {
+ ClientPoliciesResource permissions = authorization.policies().client();
+
+ try (Response response = permissions.create(representation)) {
+ Assertions.assertEquals(201, response.getStatusInfo().getStatusCode());
+ ClientPolicyRepresentation created = response.readEntity(ClientPolicyRepresentation.class);
+ ClientPolicyResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+ }
+
+ private void assertRepresentation(ClientPolicyRepresentation representation, ClientPolicyResource permission) {
+ ClientPolicyRepresentation actual = permission.toRepresentation();
+ assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+ Assertions.assertEquals(representation.getClients().size(), actual.getClients().size());
+ Assertions.assertEquals(0, actual.getClients().stream().filter(clientId -> !representation.getClients().stream()
+ .filter(userName -> getClientName(clientId).equalsIgnoreCase(userName))
+ .findFirst().isPresent())
+ .count());
+ }
+
+ private String getClientName(String id) {
+ return getRealm().clients().get(id).toRepresentation().getClientId();
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ExportAuthorizationSettingsTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ExportAuthorizationSettingsTest.java
new file mode 100644
index 0000000..95d14b7
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ExportAuthorizationSettingsTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+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.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.RoleBuilder;
+
+/**
+ *
+ * @author Vlasta Ramik
+ */
+public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
+
+ //KEYCLOAK-4341
+ @Test
+ public void testResourceBasedPermission() throws Exception {
+ String permissionName = "resource-based-permission";
+
+ ClientResource clientResource = getClientResource();
+ AuthorizationResource authorizationResource = clientResource.authorization();
+
+ //get Default Resource
+ List resources = authorizationResource.resources().findByName("Default Resource");
+ Assertions.assertTrue(resources.size() == 1);
+ ResourceRepresentation resource = resources.get(0);
+
+ //get Default Policy
+ PolicyRepresentation policy = authorizationResource.policies().findByName("Default Policy");
+
+ //create Resource-based permission and add default policy/resource
+ ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+ permission.setName(permissionName);
+ permission.addPolicy(policy.getId());
+ permission.addResource(resource.getId());
+ try (Response create = authorizationResource.permissions().resource().create(permission)) {
+ Assertions.assertEquals(Status.CREATED, create.getStatusInfo());
+ }
+
+ //export authorization settings
+ ResourceServerRepresentation exportSettings = authorizationResource.exportSettings();
+
+ //check exported settings contains both resources/applyPolicies
+ boolean found = false;
+ for (PolicyRepresentation p : exportSettings.getPolicies()) {
+ if (p.getName().equals(permissionName)) {
+ found = true;
+ Assertions.assertEquals("[\"Default Resource\"]", p.getConfig().get("resources"));
+ Assertions.assertEquals("[\"Default Policy\"]", p.getConfig().get("applyPolicies"));
+ }
+ }
+ Assertions.assertTrue(found, "Permission \"role-based-permission\" was not found.");
+ }
+
+ //KEYCLOAK-4340
+ @Test
+ public void testRoleBasedPolicy() {
+ ClientResource clientResource = getClientResource();
+ AuthorizationResource authorizationResource = clientResource.authorization();
+
+ ClientRepresentation account = testRealmResource().clients().findByClientId("account").get(0);
+ RoleRepresentation role = testRealmResource().clients().get(account.getId()).roles().get("view-profile").toRepresentation();
+
+ PolicyRepresentation policy = new PolicyRepresentation();
+ policy.setName("role-based-policy");
+ policy.setType("role");
+ Map config = new HashMap<>();
+ config.put("roles", "[{\"id\":\"" + role.getId() +"\"}]");
+ policy.setConfig(config);
+ try (Response create = authorizationResource.policies().create(policy)) {
+ Assertions.assertEquals(Status.CREATED, create.getStatusInfo());
+ }
+
+ //this call was messing up with DB, see KEYCLOAK-4340
+ authorizationResource.exportSettings();
+
+ //this call failed with NPE
+ authorizationResource.exportSettings();
+ }
+
+ //KEYCLOAK-4983
+ @Test
+ public void testRoleBasedPolicyWithMultipleRoles() {
+ ClientResource clientResource = getClientResource();
+ AuthorizationResource authorizationResource = clientResource.authorization();
+
+ testRealmResource().clients().create(ClientBuilder.create().clientId("test-client-1").build()).close();
+ testRealmResource().clients().create(ClientBuilder.create().clientId("test-client-2").build()).close();
+
+ ClientRepresentation client1 = getClientByClientId("test-client-1");
+ ClientRepresentation client2 = getClientByClientId("test-client-2");
+
+ testRealmResource().clients().get(client1.getId()).roles().create(RoleBuilder.create().name("client-role").build());
+ testRealmResource().clients().get(client2.getId()).roles().create(RoleBuilder.create().name("client-role").build());
+
+ RoleRepresentation role1 = testRealmResource().clients().get(client1.getId()).roles().get("client-role").toRepresentation();
+ RoleRepresentation role2 = testRealmResource().clients().get(client2.getId()).roles().get("client-role").toRepresentation();
+
+ PolicyRepresentation policy = new PolicyRepresentation();
+ policy.setName("role-based-policy");
+ policy.setType("role");
+ Map config = new HashMap<>();
+ config.put("roles", "[{\"id\":\"" + role1.getId() +"\"},{\"id\":\"" + role2.getId() +"\"}]");
+ policy.setConfig(config);
+ try (Response create = authorizationResource.policies().create(policy)) {
+ Assertions.assertEquals(Status.CREATED, create.getStatusInfo());
+ }
+
+ //export authorization settings
+ ResourceServerRepresentation exportSettings = authorizationResource.exportSettings();
+
+ boolean found = false;
+ for (PolicyRepresentation p : exportSettings.getPolicies()) {
+ if (p.getName().equals("role-based-policy")) {
+ found = true;
+ Assertions.assertTrue(p.getConfig().get("roles").contains("test-client-1/client-role") &&
+ p.getConfig().get("roles").contains("test-client-2/client-role"));
+ }
+ }
+ Assertions.assertTrue(found, "Policy \"role-based-policy\" was not found in exported settings.");
+ }
+
+ private ClientRepresentation getClientByClientId(String clientId) {
+ List findByClientId = testRealmResource().clients().findByClientId(clientId);
+ Assertions.assertTrue(findByClientId.size() == 1);
+ return findByClientId.get(0);
+ }
+}
\ No newline at end of file
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/GenericPolicyManagementAdminEventTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/GenericPolicyManagementAdminEventTest.java
new file mode 100644
index 0000000..ae9cadd
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/GenericPolicyManagementAdminEventTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 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.client.testsuite.authz.admin;
+
+import org.keycloak.testsuite.util.RealmBuilder;
+
+/**
+ * @author Pedro Igor
+ */
+public class GenericPolicyManagementAdminEventTest extends GenericPolicyManagementTest {
+
+ @Override
+ protected RealmBuilder createTestRealm() {
+ return super.createTestRealm()
+ .adminEvents()
+ .events();
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/GenericPolicyManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/GenericPolicyManagementTest.java
new file mode 100644
index 0000000..321ded8
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/GenericPolicyManagementTest.java
@@ -0,0 +1,325 @@
+/*
+ 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.core.Response;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.PoliciesResource;
+import org.keycloak.admin.client.resource.PolicyResource;
+import org.keycloak.admin.client.resource.ResourceResource;
+import org.keycloak.admin.client.resource.ResourceScopeResource;
+import org.keycloak.admin.client.resource.ResourceScopesResource;
+import org.keycloak.admin.client.resource.ResourcesResource;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
+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.ScopeRepresentation;
+import org.testcontainers.shaded.org.hamcrest.MatcherAssert;
+import org.testcontainers.shaded.org.hamcrest.Matchers;
+
+/**
+ * @author Pedro Igor
+ */
+public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
+
+ private static final String[] EXPECTED_BUILTIN_POLICY_PROVIDERS = {"user", "role", "time", "aggregate", "scope", "resource", "group", "client", "regex"};
+
+ @Test
+ public void testCreate() {
+ PolicyRepresentation newPolicy = createTestingPolicy().toRepresentation();
+
+ Assertions.assertEquals("Test Generic Policy", newPolicy.getName());
+ Assertions.assertEquals("scope", newPolicy.getType());
+ Assertions.assertEquals(Logic.POSITIVE, newPolicy.getLogic());
+ Assertions.assertEquals(DecisionStrategy.UNANIMOUS, newPolicy.getDecisionStrategy());
+ Assertions.assertEquals("configuration for A", newPolicy.getConfig().get("configA"));
+ Assertions.assertEquals("configuration for B", newPolicy.getConfig().get("configB"));
+ Assertions.assertEquals("configuration for C", newPolicy.getConfig().get("configC"));
+
+ List policies = getClientResource().authorization().policies().policies();
+
+ Assertions.assertEquals(6, policies.size());
+
+ assertAssociatedPolicy("Test Associated A", newPolicy);
+ assertAssociatedPolicy("Test Associated B", newPolicy);
+ assertAssociatedPolicy("Test Associated C", newPolicy);
+
+ assertAssociatedResource("Test Resource A", newPolicy);
+ assertAssociatedResource("Test Resource B", newPolicy);
+ assertAssociatedResource("Test Resource C", newPolicy);
+
+ assertAssociatedScope("Test Scope A", newPolicy);
+ assertAssociatedScope("Test Scope B", newPolicy);
+ assertAssociatedScope("Test Scope C", newPolicy);
+ }
+
+ @Test
+ public void testUpdate() {
+ PolicyResource policyResource = createTestingPolicy();
+ PolicyRepresentation policy = policyResource.toRepresentation();
+
+ policy.setName("changed");
+ policy.setLogic(Logic.NEGATIVE);
+ policy.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ policy.getConfig().put("configA", "changed configuration for A");
+ policy.getConfig().remove("configB");
+ policy.getConfig().put("configC", "changed configuration for C");
+
+ policyResource.update(policy);
+
+ policy = policyResource.toRepresentation();
+
+ Assertions.assertEquals("changed", policy.getName());
+ Assertions.assertEquals(Logic.NEGATIVE, policy.getLogic());
+
+ Assertions.assertEquals(DecisionStrategy.AFFIRMATIVE, policy.getDecisionStrategy());
+ Assertions.assertEquals("changed configuration for A", policy.getConfig().get("configA"));
+ Assertions.assertNull(policy.getConfig().get("configB"));
+ Assertions.assertEquals("changed configuration for C", policy.getConfig().get("configC"));
+
+ Map config = policy.getConfig();
+
+ config.put("applyPolicies", buildConfigOption(findPolicyByName("Test Associated C").getId()));
+
+ config.put("resources", buildConfigOption(findResourceByName("Test Resource B").getId()));
+
+ config.put("scopes", buildConfigOption(findScopeByName("Test Scope A").getId()));
+
+ policyResource.update(policy);
+
+ policy = policyResource.toRepresentation();
+
+ assertAssociatedPolicy("Test Associated C", policy);
+ List associatedPolicies = getClientResource().authorization().policies().policy(policy.getId()).associatedPolicies();
+ Assertions.assertFalse(associatedPolicies.stream().filter(associated -> associated.getId().equals(findPolicyByName("Test Associated A").getId())).findFirst().isPresent());
+ Assertions.assertFalse(associatedPolicies.stream().filter(associated -> associated.getId().equals(findPolicyByName("Test Associated B").getId())).findFirst().isPresent());
+
+ assertAssociatedResource("Test Resource B", policy);
+ List resources = policyResource.resources();
+ Assertions.assertFalse(resources.contains(findResourceByName("Test Resource A")));
+ Assertions.assertFalse(resources.contains(findResourceByName("Test Resource C")));
+
+ assertAssociatedScope("Test Scope A", policy);
+ List scopes = getClientResource().authorization().policies().policy(policy.getId()).scopes();
+ Assertions.assertFalse(scopes.contains(findScopeByName("Test Scope B")));
+ Assertions.assertFalse(scopes.contains(findScopeByName("Test Scope C")));
+ }
+
+ @Test
+ public void testDefaultPolicyProviders() {
+ List providers = getClientResource().authorization().policies()
+ .policyProviders().stream().map(PolicyProviderRepresentation::getType)
+ .collect(Collectors.toList());
+
+ Assertions.assertFalse(providers.isEmpty());
+ MatcherAssert.assertThat(providers, Matchers.hasItems(EXPECTED_BUILTIN_POLICY_PROVIDERS));
+ }
+
+ @Test
+ public void testQueryPolicyByIdAllFields() {
+ PolicyResource policy = createTestingPolicy();
+ PolicyRepresentation representation = policy.toRepresentation("*");
+ Set resources = representation.getResourcesData();
+
+ Assertions.assertEquals(3, resources.size());
+
+ representation = policy.toRepresentation();
+ Assertions.assertNull(representation.getResourcesData());
+ }
+
+ @Test
+ public void testQueryPolicyAllFields() {
+ AuthorizationResource authorization = getClientResource().authorization();
+
+ authorization.resources().create(new ResourceRepresentation("Resource A"));
+ ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+ permission.setName("Permission A");
+ permission.addResource("Resource A");
+ authorization.permissions().resource().create(permission);
+
+ List policies = authorization.policies()
+ .policies(null, "Permission A", null, null, null, true, null, "*", -1, -1);
+
+ Assertions.assertEquals(1, policies.size());
+ Assertions.assertEquals(1, policies.get(0).getResourcesData().size());
+
+ policies = authorization.policies()
+ .policies(null, "Permission A", null, null, null, true, null, null, -1, -1);
+
+ Assertions.assertEquals(1, policies.size());
+ Assertions.assertNull(policies.get(0).getResourcesData());
+ }
+
+ private PolicyResource createTestingPolicy() {
+ Map config = new HashMap<>();
+
+ config.put("configA", "configuration for A");
+ config.put("configB", "configuration for B");
+ config.put("configC", "configuration for C");
+
+ config.put("applyPolicies", buildConfigOption(
+ createPolicy("Test Associated A", new HashMap<>()).toRepresentation().getId(),
+ createPolicy("Test Associated B", new HashMap<>()).toRepresentation().getId(),
+ createPolicy("Test Associated C", new HashMap<>()).toRepresentation().getId()
+ ));
+
+ config.put("resources", buildConfigOption(
+ createResource("Test Resource A").toRepresentation().getId(),
+ createResource("Test Resource B").toRepresentation().getId(),
+ createResource("Test Resource C").toRepresentation().getId()
+ ));
+
+ config.put("scopes", buildConfigOption(
+ createScope("Test Scope A").toRepresentation().getId(),
+ createScope("Test Scope B").toRepresentation().getId(),
+ createScope("Test Scope C").toRepresentation().getId()
+ ));
+
+ return createPolicy("Test Generic Policy", config);
+ }
+
+ private PolicyResource createPolicy(String name, Map config) {
+ PolicyRepresentation newPolicy = new PolicyRepresentation();
+
+ newPolicy.setName(name);
+ newPolicy.setType("scope");
+ newPolicy.setConfig(config);
+
+ PoliciesResource policies = getClientResource().authorization().policies();
+
+ try (Response response = policies.create(newPolicy)) {
+ Assertions.assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+
+ PolicyRepresentation stored = response.readEntity(PolicyRepresentation.class);
+
+ return policies.policy(stored.getId());
+ }
+ }
+
+ private ResourceResource createResource(String name) {
+ ResourceRepresentation newResource = new ResourceRepresentation();
+
+ newResource.setName(name);
+
+ ResourcesResource resources = getClientResource().authorization().resources();
+
+ try (Response response = resources.create(newResource)) {
+ Assertions.assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+
+ ResourceRepresentation stored = response.readEntity(ResourceRepresentation.class);
+
+ return resources.resource(stored.getId());
+ }
+ }
+
+ private ResourceScopeResource createScope(String name) {
+ ScopeRepresentation newScope = new ScopeRepresentation();
+
+ newScope.setName(name);
+
+ ResourceScopesResource scopes = getClientResource().authorization().scopes();
+
+ try (Response response = scopes.create(newScope)) {
+
+ Assertions.assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+
+ ScopeRepresentation stored = response.readEntity(ScopeRepresentation.class);
+
+ return scopes.scope(stored.getId());
+ }
+ }
+
+ private String buildConfigOption(String... values) {
+ StringBuilder builder = new StringBuilder();
+
+ for (String value : values) {
+ if (builder.length() > 0) {
+ builder.append(",");
+ }
+ builder.append("\"").append(value).append("\"");
+ }
+
+ return builder.insert(0, "[").append("]").toString();
+ }
+
+ private PolicyRepresentation findPolicyByName(String name) {
+ return getClientResource().authorization().policies().policies()
+ .stream().filter(policyRepresentation -> policyRepresentation.getName().equals(name))
+ .findFirst().orElse(null);
+ }
+
+ private ResourceRepresentation findResourceByName(String name) {
+ return getClientResource().authorization().resources().resources()
+ .stream().filter(resource -> resource.getName().equals(name))
+ .findFirst().orElse(null);
+ }
+
+ private ScopeRepresentation findScopeByName(String name) {
+ return getClientResource().authorization().scopes().scopes()
+ .stream().filter(scope -> scope.getName().equals(name))
+ .findFirst().orElse(null);
+ }
+
+ private void assertAssociatedPolicy(String associatedPolicyName, PolicyRepresentation dependentPolicy) {
+ PolicyRepresentation associatedPolicy = findPolicyByName(associatedPolicyName);
+ PoliciesResource policies = getClientResource().authorization().policies();
+ associatedPolicy = policies.policy(associatedPolicy.getId()).toRepresentation();
+ Assertions.assertNotNull(associatedPolicy);
+ PolicyRepresentation finalAssociatedPolicy = associatedPolicy;
+ PolicyResource policyResource = policies.policy(dependentPolicy.getId());
+ List associatedPolicies = policyResource.associatedPolicies();
+ Assertions.assertTrue(associatedPolicies.stream().filter(associated -> associated.getId().equals(finalAssociatedPolicy.getId())).findFirst().isPresent());
+ List dependentPolicies = policies.policy(associatedPolicy.getId()).dependentPolicies();
+ Assertions.assertEquals(1, dependentPolicies.size());
+ Assertions.assertEquals(dependentPolicy.getId(), dependentPolicies.get(0).getId());
+ }
+
+ private void assertAssociatedResource(String resourceName, PolicyRepresentation policy) {
+ ResourceRepresentation resource = findResourceByName(resourceName);
+ Assertions.assertNotNull(resource);
+ List resources = getClientResource().authorization().policies().policy(policy.getId()).resources();
+ Assertions.assertTrue(resources.contains(resource));
+ List policies = getClientResource().authorization().resources().resource(resource.getId()).permissions();
+ Assertions.assertEquals(1, policies.size());
+ Assertions.assertTrue(policies.stream().map(PolicyRepresentation::getId).collect(Collectors.toList())
+ .contains(policy.getId()));
+ }
+
+ private void assertAssociatedScope(String scopeName, PolicyRepresentation policy) {
+ ScopeRepresentation scope = findScopeByName(scopeName);
+ scope = getClientResource().authorization().scopes().scope(scope.getId()).toRepresentation();
+ Assertions.assertNotNull(scope);
+ List scopes = getClientResource().authorization().policies().policy(policy.getId()).scopes();
+ Assertions.assertTrue(scopes.stream().map((Function) rep -> rep.getId()).collect(Collectors.toList()).contains(scope.getId()));
+ List permissions = getClientResource().authorization().scopes().scope(scope.getId()).permissions();
+ Assertions.assertEquals(1, permissions.size());
+ Assertions.assertTrue(permissions.stream().map(PolicyRepresentation::getId).collect(Collectors.toList())
+ .contains(policy.getId()));
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/GroupPolicyManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/GroupPolicyManagementTest.java
new file mode 100644
index 0000000..cd7baa9
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/GroupPolicyManagementTest.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2017 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.GroupPoliciesResource;
+import org.keycloak.admin.client.resource.GroupPolicyResource;
+import org.keycloak.admin.client.resource.GroupsResource;
+import org.keycloak.admin.client.resource.PolicyResource;
+import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.testsuite.util.GroupBuilder;
+import org.keycloak.testsuite.util.KeycloakModelUtils;
+import org.keycloak.testsuite.util.RealmBuilder;
+
+/**
+ * @author Pedro Igor
+ */
+public class GroupPolicyManagementTest extends AbstractPolicyManagementTest {
+
+ @Override
+ protected RealmBuilder createTestRealm() {
+ return super.createTestRealm().group(GroupBuilder.create().name("Group A")
+ .subGroups(Arrays.asList("Group B", "Group D").stream().map(name -> {
+ if ("Group B".equals(name)) {
+ return GroupBuilder.create().name(name).subGroups(Arrays.asList("Group C", "Group E").stream().map(name1 -> GroupBuilder.create().name(name1).build()).collect(Collectors.toList())).build();
+ }
+ return GroupBuilder.create().name(name).build();
+ }).collect(Collectors.toList()))
+ .build()).group(GroupBuilder.create().name("Group F").build())
+ .group(GroupBuilder.create().name("Group G").build())
+ .group(GroupBuilder.create().name("Group H")
+ .subGroups(Arrays.asList("Group I", "Group J").stream().map(name -> {
+ if ("Group I".equals(name)) {
+ return GroupBuilder.create().name(name).subGroups(Arrays.asList("Group K", "Group L").stream().map(name1 -> GroupBuilder.create().name(name1).build()).collect(Collectors.toList())).build();
+ }
+ return GroupBuilder.create().name(name).build();
+ }).collect(Collectors.toList()))
+ .build());
+ }
+
+ @Test
+ public void testCreate() {
+ AuthorizationResource authorization = getClient().authorization();
+ GroupPolicyRepresentation representation = new GroupPolicyRepresentation();
+
+ representation.setName("Group Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.setGroupsClaim("groups");
+ representation.addGroupPath("/Group A/Group B/Group C", true);
+ representation.addGroupPath("Group F");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testCreateWithoutGroupsClaim() {
+ AuthorizationResource authorization = getClient().authorization();
+ GroupPolicyRepresentation representation = new GroupPolicyRepresentation();
+
+ representation.setName(KeycloakModelUtils.generateId());
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addGroupPath("/Group A/Group B/Group C", true);
+ representation.addGroupPath("Group F");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ GroupPolicyRepresentation representation = new GroupPolicyRepresentation();
+
+ representation.setName("Update Group Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.setGroupsClaim("groups");
+ representation.addGroupPath("/Group A/Group B/Group C", true);
+ representation.addGroupPath("Group F");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.setGroupsClaim(null);
+ representation.removeGroup("/Group A/Group B");
+
+ GroupPoliciesResource policies = authorization.policies().group();
+ GroupPolicyResource permission = policies.findById(representation.getId());
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+
+ for (GroupPolicyRepresentation.GroupDefinition roleDefinition : representation.getGroups()) {
+ if (roleDefinition.getPath().equals("Group F")) {
+ roleDefinition.setExtendChildren(true);
+ }
+ }
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+
+ representation.getGroups().clear();
+ representation.addGroupPath("/Group A/Group B");
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ GroupPolicyRepresentation representation = new GroupPolicyRepresentation();
+
+ representation.setName("Delete Group Policy");
+ representation.setGroupsClaim("groups");
+ representation.addGroupPath("/Group A/Group B/Group C", true);
+ representation.addGroupPath("Group F");
+
+ GroupPoliciesResource policies = authorization.policies().group();
+
+ try (Response response = policies.create(representation)) {
+ GroupPolicyRepresentation created = response.readEntity(GroupPolicyRepresentation.class);
+
+ policies.findById(created.getId()).remove();
+
+ GroupPolicyResource removed = policies.findById(created.getId());
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class, () -> removed.toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+ }
+
+ @Test
+ public void testRemoveWithoutPath() {
+ GroupPolicyRepresentation representation = new GroupPolicyRepresentation();
+
+ representation.setName("Delete Group Path Policy");
+ representation.setGroupsClaim("groups");
+ representation.addGroup("Group A");
+
+ representation.removeGroup("Group A");
+
+ Assertions.assertTrue(representation.getGroups().isEmpty());
+ }
+
+ @Test
+ public void testGenericConfig() {
+ AuthorizationResource authorization = getClient().authorization();
+ GroupPolicyRepresentation representation = new GroupPolicyRepresentation();
+
+ representation.setName("Test Generic Config Permission");
+ representation.setGroupsClaim("groups");
+ representation.addGroupPath("/Group A");
+
+ GroupPoliciesResource policies = authorization.policies().group();
+
+ try (Response response = policies.create(representation)) {
+ GroupPolicyRepresentation created = response.readEntity(GroupPolicyRepresentation.class);
+
+ PolicyResource policy = authorization.policies().policy(created.getId());
+ PolicyRepresentation genericConfig = policy.toRepresentation();
+
+ Assertions.assertNotNull(genericConfig.getConfig());
+ Assertions.assertNotNull(genericConfig.getConfig().get("groups"));
+
+ GroupRepresentation group = getRealm().groups().groups().stream().filter(groupRepresentation -> groupRepresentation.getName().equals("Group A")).findFirst().get();
+
+ Assertions.assertTrue(genericConfig.getConfig().get("groups").contains(group.getId()));
+ }
+ }
+
+ @Test
+ public void testDeleteGroupAndPolicy() {
+ AuthorizationResource authorization = getClient().authorization();
+ GroupPolicyRepresentation representation = new GroupPolicyRepresentation();
+
+ representation.setName(UUID.randomUUID().toString());
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.setGroupsClaim("groups");
+ representation.addGroupPath("/Group G", true);
+
+ assertCreated(authorization, representation);
+
+ GroupsResource groups = getRealm().groups();
+ GroupRepresentation group = groups.groups("Group G", null, null).get(0);
+
+ groups.group(group.getId()).remove();
+
+ GroupPolicyRepresentation actual = getClient().authorization().policies().group().findByName(representation.getName());
+ Assertions.assertEquals(0, actual.getGroups().size());
+ }
+
+ private void assertCreated(AuthorizationResource authorization, GroupPolicyRepresentation representation) {
+ GroupPoliciesResource policies = authorization.policies().group();
+ Response response = policies.create(representation);
+ GroupPolicyRepresentation created = response.readEntity(GroupPolicyRepresentation.class);
+ GroupPolicyResource policy = policies.findById(created.getId());
+ assertRepresentation(representation, policy);
+ }
+
+ private void assertRepresentation(GroupPolicyRepresentation representation, GroupPolicyResource permission) {
+ GroupPolicyRepresentation actual = permission.toRepresentation();
+ assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+ Assertions.assertEquals(representation.getGroups().size(), actual.getGroups().size());
+ Assertions.assertEquals(0, actual.getGroups().stream().filter(actualDefinition -> !representation.getGroups().stream()
+ .filter(groupDefinition -> getGroupPath(actualDefinition.getId()).equals(getCanonicalGroupPath(groupDefinition.getPath())) && actualDefinition.isExtendChildren() == groupDefinition.isExtendChildren())
+ .findFirst().isPresent())
+ .count());
+ }
+
+ private String getGroupPath(String id) {
+ return getRealm().groups().group(id).toRepresentation().getPath();
+ }
+
+ private String getCanonicalGroupPath(String path) {
+ if (path.charAt(0) == '/') {
+ return path;
+ }
+ return "/" + path;
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ImportAuthorizationSettingsTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ImportAuthorizationSettingsTest.java
new file mode 100644
index 0000000..e253825
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ImportAuthorizationSettingsTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.testsuite.util.UserBuilder;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ *
+ * @author Pedro Igor
+ */
+public class ImportAuthorizationSettingsTest extends AbstractAuthorizationTest {
+
+ @BeforeEach
+ public void createRole() {
+ ClientResource clientResource = getClientResource();
+
+ RoleRepresentation role = new RoleRepresentation();
+ role.setName("admin");
+ clientResource.roles().create(role);
+
+ testRealmResource().users().create(UserBuilder.create().username("alice").build());
+ }
+
+ @Test
+ public void testImportUnorderedSettings() throws Exception {
+ ClientResource clientResource = getClientResource();
+ ResourceServerRepresentation toImport = JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/import-authorization-unordered-settings.json"), ResourceServerRepresentation.class);
+
+ testRealmResource().roles().create(new RoleRepresentation("user", null, false));
+ clientResource.roles().create(new RoleRepresentation("manage-albums", null, false));
+
+ AuthorizationResource authorizationResource = clientResource.authorization();
+
+ authorizationResource.importSettings(toImport);
+
+ Assertions.assertEquals(13, authorizationResource.policies().policies().size());
+ }
+}
\ No newline at end of file
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/JSPolicyManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/JSPolicyManagementTest.java
new file mode 100644
index 0000000..b516895
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/JSPolicyManagementTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.Collections;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.JSPoliciesResource;
+import org.keycloak.admin.client.resource.JSPolicyResource;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+
+/**
+ * @author Pedro Igor
+ */
+public class JSPolicyManagementTest extends AbstractPolicyManagementTest {
+
+ @Test
+ public void testCreate() {
+ AuthorizationResource authorization = getClient().authorization();
+ JSPolicyRepresentation representation = new JSPolicyRepresentation();
+
+ representation.setName("JS Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.setType("script-scripts/default-policy.js");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ JSPolicyRepresentation representation = new JSPolicyRepresentation();
+
+ representation.setName("Update JS Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.setType("script-scripts/default-policy.js");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+
+ JSPoliciesResource policies = authorization.policies().js();
+ JSPolicyResource permission = policies.findById(representation.getId());
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ JSPolicyRepresentation representation = new JSPolicyRepresentation();
+
+ representation.setName("Test Delete Policy");
+ representation.setType("script-scripts/default-policy.js");
+
+ JSPoliciesResource policies = authorization.policies().js();
+ try (Response response = policies.create(representation)) {
+ JSPolicyRepresentation created = response.readEntity(JSPolicyRepresentation.class);
+
+ policies.findById(created.getId()).remove();
+
+ JSPolicyResource removed = policies.findById(created.getId());
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class, () -> removed.toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+ }
+
+ private void assertCreated(AuthorizationResource authorization, JSPolicyRepresentation representation) {
+ JSPoliciesResource permissions = authorization.policies().js();
+
+ try (Response response = permissions.create(representation)) {
+ JSPolicyRepresentation created = response.readEntity(JSPolicyRepresentation.class);
+ JSPolicyResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+ }
+
+ private void assertRepresentation(JSPolicyRepresentation representation, JSPolicyResource permission) {
+ JSPolicyRepresentation actual = permission.toRepresentation();
+ assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+ Assertions.assertEquals(representation.getType(), actual.getType());
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourceManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourceManagementTest.java
new file mode 100644
index 0000000..db281bd
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourceManagementTest.java
@@ -0,0 +1,419 @@
+/*
+ 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.ResourceResource;
+import org.keycloak.admin.client.resource.ResourcesResource;
+import org.keycloak.authorization.client.util.HttpResponseException;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.testcontainers.shaded.org.hamcrest.MatcherAssert;
+import org.testcontainers.shaded.org.hamcrest.Matchers;
+
+/**
+ *
+ * @author Pedro Igor
+ */
+public class ResourceManagementTest extends AbstractAuthorizationTest {
+
+ @Test
+ public void testCreate() {
+ ResourceRepresentation newResource = createResource();
+
+ Assertions.assertEquals("Test Resource", newResource.getName());
+ Assertions.assertEquals("/test/*", newResource.getUri());
+ Assertions.assertEquals("test-resource", newResource.getType());
+ Assertions.assertEquals("icon-test-resource", newResource.getIconUri());
+
+ Map> attributes = newResource.getAttributes();
+
+ Assertions.assertEquals(2, attributes.size());
+
+ Assertions.assertTrue(attributes.containsKey("a"));
+ Assertions.assertTrue(attributes.containsKey("b"));
+ Assertions.assertTrue(attributes.get("a").containsAll(Arrays.asList("a1", "a2", "a3")));
+ Assertions.assertEquals(3, attributes.get("a").size());
+ Assertions.assertTrue(attributes.get("b").containsAll(Arrays.asList("b1")));
+ Assertions.assertEquals(1, attributes.get("b").size());
+ }
+
+ @Test
+ public void testCreateWithResourceType() {
+ ResourceRepresentation newResource = new ResourceRepresentation();
+
+ newResource.setName("test");
+ newResource.setDisplayName("display");
+ newResource.setType("some-type");
+
+ newResource = doCreateResource(newResource);
+
+ ResourceResource resource = getClientResource().authorization().resources().resource(newResource.getId());
+
+ Assertions.assertTrue(resource.permissions().isEmpty());
+ }
+
+ @Test
+ public void testQueryAssociatedPermissions() {
+ ResourceRepresentation newResource = new ResourceRepresentation();
+
+ newResource.setName("r1");
+ newResource.setType("some-type");
+ newResource.addScope("GET");
+
+ newResource = doCreateResource(newResource);
+
+ ResourceResource resource = getClientResource().authorization().resources().resource(newResource.getId());
+
+ ScopePermissionRepresentation permission = new ScopePermissionRepresentation();
+
+ permission.setName(newResource.getName());
+ permission.addResource(newResource.getName());
+ permission.addScope("GET");
+
+ getClientResource().authorization().permissions().scope().create(permission);
+
+ Assertions.assertFalse(resource.permissions().isEmpty());
+ }
+
+ @Test
+ public void testQueryTypedResourcePermissions() {
+ ResourceRepresentation r1 = new ResourceRepresentation();
+
+ r1.setName("r1");
+ r1.setType("some-type");
+ r1.addScope("GET");
+
+ r1 = doCreateResource(r1);
+
+ ScopePermissionRepresentation permission = new ScopePermissionRepresentation();
+
+ permission.setName(r1.getName());
+ permission.addResource(r1.getName());
+ permission.addScope("GET");
+
+ getClientResource().authorization().permissions().scope().create(permission);
+
+ ResourceRepresentation r2 = new ResourceRepresentation();
+
+ r2.setName("r2");
+ r2.setType("some-type");
+ r2.addScope("GET");
+
+ r2 = doCreateResource(r2);
+
+ permission = new ScopePermissionRepresentation();
+
+ permission.setName(r2.getName());
+ permission.addResource(r2.getName());
+ permission.addScope("GET");
+
+ getClientResource().authorization().permissions().scope().create(permission);
+
+ ResourceResource resource2 = getClientResource().authorization().resources().resource(r2.getId());
+ List permissions = resource2.permissions();
+
+ Assertions.assertEquals(1, permissions.size());
+ Assertions.assertEquals(r2.getName(), permissions.get(0).getName());
+
+ ResourceResource resource1 = getClientResource().authorization().resources().resource(r1.getId());
+
+ permissions = resource1.permissions();
+
+ Assertions.assertEquals(1, permissions.size());
+ Assertions.assertEquals(r1.getName(), permissions.get(0).getName());
+ }
+
+ @Test
+ public void testQueryTypedResourcePermissionsForResourceInstances() {
+ ResourceRepresentation r1 = new ResourceRepresentation();
+
+ r1.setName("r1");
+ r1.setType("some-type");
+ r1.addScope("GET");
+
+ r1 = doCreateResource(r1);
+
+ ScopePermissionRepresentation permission = new ScopePermissionRepresentation();
+
+ permission.setName(r1.getName());
+ permission.addResource(r1.getName());
+ permission.addScope("GET");
+
+ getClientResource().authorization().permissions().scope().create(permission);
+
+ ResourceRepresentation r2 = new ResourceRepresentation();
+
+ r2.setName("r2");
+ r2.setType("some-type");
+ r2.addScope("GET");
+
+ r2 = doCreateResource(r2);
+
+ permission = new ScopePermissionRepresentation();
+
+ permission.setName(r2.getName());
+ permission.addResource(r2.getName());
+ permission.addScope("GET");
+
+ getClientResource().authorization().permissions().scope().create(permission);
+
+ ResourceRepresentation rInstance = new ResourceRepresentation();
+
+ rInstance.setName("rInstance");
+ rInstance.setType("some-type");
+ rInstance.setOwner("marta");
+ rInstance.addScope("GET", "POST");
+
+ rInstance = doCreateResource(rInstance);
+
+ List permissions = getClientResource().authorization().resources().resource(rInstance.getId()).permissions();
+
+ Assertions.assertEquals(2, permissions.size());
+
+ permission = new ScopePermissionRepresentation();
+
+ permission.setName("POST permission");
+ permission.addScope("POST");
+
+ getClientResource().authorization().permissions().scope().create(permission);
+
+ permissions = getClientResource().authorization().resources().resource(rInstance.getId()).permissions();
+
+ Assertions.assertEquals(3, permissions.size());
+ }
+
+ @Test
+ public void failCreateWithSameName() {
+ final ResourceRepresentation newResource1 = createResource();
+
+ RuntimeException re = Assertions.assertThrows(RuntimeException.class, () -> doCreateResource(newResource1));
+ MatcherAssert.assertThat(re.getCause(), Matchers.instanceOf(HttpResponseException.class));
+ Assertions.assertEquals(409, HttpResponseException.class.cast(re.getCause()).getStatusCode());
+
+ newResource1.setName(newResource1.getName() + " Another");
+
+ final ResourceRepresentation newResource2 = doCreateResource(newResource1);
+
+ Assertions.assertNotNull(newResource2.getId());
+ Assertions.assertEquals("Test Resource Another", newResource2.getName());
+ }
+
+ @Test
+ public void failCreateWithSameNameDifferentOwner() {
+ ResourceRepresentation martaResource = createResource("Resource A", "marta", null, null, null);
+ ResourceRepresentation koloResource = createResource("Resource A", "kolo", null, null, null);
+
+ Assertions.assertNotNull(martaResource.getId());
+ Assertions.assertNotNull(koloResource.getId());
+ Assertions.assertNotEquals(martaResource.getId(), koloResource.getId());
+
+ Assertions.assertEquals(2, getClientResource().authorization().resources().findByName(martaResource.getName()).size());
+
+ List martaResources = getClientResource().authorization().resources().findByName(martaResource.getName(), "marta");
+
+ Assertions.assertEquals(1, martaResources.size());
+ Assertions.assertEquals(martaResource.getId(), martaResources.get(0).getId());
+
+ List koloResources = getClientResource().authorization().resources().findByName(martaResource.getName(), "kolo");
+
+ Assertions.assertEquals(1, koloResources.size());
+ Assertions.assertEquals(koloResource.getId(), koloResources.get(0).getId());
+ }
+
+ @Test
+ public void testUpdate() {
+ ResourceRepresentation resource = createResource();
+
+ resource.setType("changed");
+ resource.setIconUri("changed");
+ resource.setUri("changed");
+
+ Map> attributes = resource.getAttributes();
+
+ attributes.remove("a");
+ attributes.put("c", Arrays.asList("c1", "c2"));
+ attributes.put("b", Arrays.asList("changed"));
+
+ resource = doUpdateResource(resource);
+
+ Assertions.assertEquals("changed", resource.getIconUri());
+ Assertions.assertEquals("changed", resource.getType());
+ Assertions.assertEquals("changed", resource.getUri());
+
+ attributes = resource.getAttributes();
+
+ Assertions.assertEquals(2, attributes.size());
+
+ Assertions.assertFalse(attributes.containsKey("a"));
+ Assertions.assertTrue(attributes.containsKey("b"));
+ Assertions.assertTrue(attributes.get("b").containsAll(Arrays.asList("changed")));
+ Assertions.assertEquals(1, attributes.get("b").size());
+ Assertions.assertTrue(attributes.get("c").containsAll(Arrays.asList("c1", "c2")));
+ Assertions.assertEquals(2, attributes.get("c").size());
+ }
+
+ @Test
+ public void testDelete() {
+ ResourceRepresentation resource = createResource();
+
+ doRemoveResource(resource);
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class,
+ () -> getClientResource().authorization().resources().resource(resource.getId()).toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+
+ @Test
+ public void testAssociateScopes() {
+ ResourceRepresentation updated = createResourceWithDefaultScopes();
+
+ Assertions.assertEquals(3, updated.getScopes().size());
+
+ Assertions.assertTrue(containsScope("Scope A", updated));
+ Assertions.assertTrue(containsScope("Scope B", updated));
+ Assertions.assertTrue(containsScope("Scope C", updated));
+ }
+
+ @Test
+ public void testUpdateScopes() {
+ ResourceRepresentation resource = createResourceWithDefaultScopes();
+ Set scopes = new HashSet<>(resource.getScopes());
+
+ Assertions.assertEquals(3, scopes.size());
+ Assertions.assertTrue(scopes.removeIf(scopeRepresentation -> scopeRepresentation.getName().equals("Scope B")));
+
+ resource.setScopes(scopes);
+
+ ResourceRepresentation updated = doUpdateResource(resource);
+
+ Assertions.assertEquals(2, resource.getScopes().size());
+
+ Assertions.assertFalse(containsScope("Scope B", updated));
+ Assertions.assertTrue(containsScope("Scope A", updated));
+ Assertions.assertTrue(containsScope("Scope C", updated));
+
+ scopes = new HashSet<>(updated.getScopes());
+
+ Assertions.assertTrue(scopes.removeIf(scopeRepresentation -> scopeRepresentation.getName().equals("Scope A")));
+ Assertions.assertTrue(scopes.removeIf(scopeRepresentation -> scopeRepresentation.getName().equals("Scope C")));
+
+ updated.setScopes(scopes);
+
+ updated = doUpdateResource(updated);
+
+ Assertions.assertEquals(0, updated.getScopes().size());
+ }
+
+ private ResourceRepresentation createResourceWithDefaultScopes() {
+ ResourceRepresentation resource = createResource();
+
+ Assertions.assertEquals(0, resource.getScopes().size());
+
+ HashSet scopes = new HashSet<>();
+
+ scopes.add(createScope("Scope A", "").toRepresentation());
+ scopes.add(createScope("Scope B", "").toRepresentation());
+ scopes.add(createScope("Scope C", "").toRepresentation());
+
+ resource.setScopes(scopes);
+
+ return doUpdateResource(resource);
+ }
+
+ private boolean containsScope(String scopeName, ResourceRepresentation resource) {
+ Set scopes = resource.getScopes();
+
+ if (scopes != null) {
+ for (ScopeRepresentation scope : scopes) {
+ if (scope.getName().equals(scopeName)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private ResourceRepresentation createResource() {
+ return createResource("Test Resource", null, "/test/*", "test-resource", "icon-test-resource");
+ }
+
+ private ResourceRepresentation createResource(String name, String owner, String uri, String type, String iconUri) {
+ ResourceRepresentation newResource = new ResourceRepresentation();
+
+ newResource.setName(name);
+ newResource.setUri(uri);
+ newResource.setType(type);
+ newResource.setIconUri(iconUri);
+ newResource.setOwner(owner != null ? new ResourceOwnerRepresentation(owner) : null);
+
+ Map> attributes = new HashMap<>();
+
+ attributes.put("a", Arrays.asList("a1", "a2", "a3"));
+ attributes.put("b", Arrays.asList("b1"));
+
+ newResource.setAttributes(attributes);
+
+ return doCreateResource(newResource);
+ }
+
+ protected ResourceRepresentation doCreateResource(ResourceRepresentation newResource) {
+ ResourcesResource resources = getClientResource().authorization().resources();
+
+ try (Response response = resources.create(newResource)) {
+
+ int status = response.getStatus();
+
+ if (status != Response.Status.CREATED.getStatusCode()) {
+ throw new RuntimeException(new HttpResponseException("Error", status, "", null));
+ }
+
+ ResourceRepresentation stored = response.readEntity(ResourceRepresentation.class);
+
+ return resources.resource(stored.getId()).toRepresentation();
+ }
+ }
+
+ protected ResourceRepresentation doUpdateResource(ResourceRepresentation resource) {
+ ResourcesResource resources = getClientResource().authorization().resources();
+ ResourceResource existing = resources.resource(resource.getId());
+
+ existing.update(resource);
+
+ return resources.resource(resource.getId()).toRepresentation();
+ }
+
+ protected void doRemoveResource(ResourceRepresentation resource) {
+ ResourcesResource resources = getClientResource().authorization().resources();
+ resources.resource(resource.getId()).remove();
+ }
+}
\ No newline at end of file
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourceManagementWithAuthzClientTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourceManagementWithAuthzClientTest.java
new file mode 100644
index 0000000..679d780
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourceManagementWithAuthzClientTest.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2017 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.client.testsuite.authz.admin;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.testcontainers.shaded.org.hamcrest.MatcherAssert;
+import org.testcontainers.shaded.org.hamcrest.Matchers;
+
+/**
+ *
+ * @author Pedro Igor
+ */
+public class ResourceManagementWithAuthzClientTest extends ResourceManagementTest {
+
+ private AuthzClient authzClient;
+
+ @Test
+ public void testFindMatchingUri() {
+ doCreateResource(new ResourceRepresentation("/*", Collections.emptySet(), "/*", null));
+ doCreateResource(new ResourceRepresentation("/resources/*", Collections.emptySet(), "/resources/*", null));
+ doCreateResource(new ResourceRepresentation("/resources-a/*", Collections.emptySet(), "/resources-a/*", null));
+ doCreateResource(new ResourceRepresentation("/resources-b/{pattern}", Collections.emptySet(), "/resources-b/{pattern}", null));
+ doCreateResource(new ResourceRepresentation("/resources-c/{pattern}/*", Collections.emptySet(), "/resources-c/{pattern}/*", null));
+ doCreateResource(new ResourceRepresentation("/resources/{pattern}/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/{pattern}/*", null));
+ doCreateResource(new ResourceRepresentation("/resources/{pattern}/sub-resources/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/sub-resources/{pattern}/*", null));
+ doCreateResource(new ResourceRepresentation("/resources/{pattern}/sub-resource", Collections.emptySet(), "/resources/{pattern}/sub-resources/{pattern}/*", null));
+ doCreateResource(new ResourceRepresentation("/rest/{version}/loader/loadTwo", Collections.emptySet(), "/rest/{version}/loader/loadTwo", null));
+ doCreateResource(new ResourceRepresentation("/rest/{version}/loader/load", Collections.emptySet(), "/rest/{version}/loader/load", null));
+ doCreateResource(new ResourceRepresentation(
+ "/rest/{version}/carts/{cartId}/cartactions/{actionId}", Collections.emptySet(), "/rest/{version}/carts/{cartId}/cartactions/{actionId}", null));
+ doCreateResource(new ResourceRepresentation("/rest/v1/carts/{cartId}/cartactions/123", Collections.emptySet(), "/rest/v1/carts/{cartId}/cartactions/123", null));
+ doCreateResource(new ResourceRepresentation("Dummy Name", Collections.emptySet(),
+ new HashSet<>(Arrays.asList("/dummy/605dc7ff310256017a2ec84f", "/dummy/605dc7ff310256017a2ec84f/*")), null));
+
+ getAuthzClient();
+
+ List resources = authzClient.protection().resource().findByMatchingUri("/test");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/*", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/resources-a/test");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/resources-a/*", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/resources");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/resources/*", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/resources/");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/resources/*", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/resources-b/a");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/resources-b/{pattern}", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/resources-c/a/b");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/resources-c/{pattern}/*", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/resources/a/b/c");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/resources/{pattern}/{pattern}/*", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/resources/a/sub-resources/c/d");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/resources/{pattern}/sub-resources/{pattern}/*", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/rest/v1/loader/load");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/rest/{version}/loader/load", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/rest/v2/carts/123/cartactions/123");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/rest/{version}/carts/{cartId}/cartactions/{actionId}", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/rest/v2/carts/{cartId}/cartactions/123");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/rest/{version}/carts/{cartId}/cartactions/{actionId}", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/rest/{version}/carts/123/cartactions/123");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/rest/{version}/carts/{cartId}/cartactions/{actionId}", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/rest/{version}/carts/{cartId}/cartactions/123");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/rest/{version}/carts/{cartId}/cartactions/{actionId}", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/rest/v1/carts/123/cartactions/123");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/rest/v1/carts/{cartId}/cartactions/123", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/rest/v1/carts/{cartId}/cartactions/123");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/rest/v1/carts/{cartId}/cartactions/123", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/rest/v1/carts/345/cartactions/123");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/rest/v1/carts/{cartId}/cartactions/123", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/rest/v2/carts/345/cartactions/123");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/rest/{version}/carts/{cartId}/cartactions/{actionId}", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/dummy/605dc7ff310256017a2ec84f/nestedObject/605dc7fe310256017a2ec84c");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("Dummy Name", resources.get(0).getName());
+ }
+
+ @Test
+ public void testUpdateUri() {
+ getAuthzClient();
+
+ doRemoveResource(authzClient.protection().resource().findByName("Default Resource"));
+
+ doCreateResource(new ResourceRepresentation("/api/v1/*", Collections.emptySet(), "/api/v1/*", null));
+
+ List resources = authzClient.protection().resource().findByMatchingUri("/api/v1/servers");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/api/v1/*", resources.get(0).getUri());
+
+ resources.get(0).getUris().clear();
+ resources.get(0).getUris().add("/api/v2/*");
+
+ authzClient.protection().resource().update(resources.get(0));
+
+ resources = authzClient.protection().resource().findByMatchingUri("/api/v1/servers");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(0, resources.size());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/api/v2");
+
+ Assertions.assertNotNull(resources);
+ Assertions.assertEquals(1, resources.size());
+ Assertions.assertEquals("/api/v2/*", resources.get(0).getUri());
+ }
+
+ @Test
+ public void testFindDeep() {
+ ResourceRepresentation resource1 = new ResourceRepresentation("/*", new HashSet<>());
+
+ resource1.addScope("a", "b", "c");
+ resource1.setType("type");
+
+ Map> attributes = new HashMap<>();
+
+ attributes.put("a", Arrays.asList("a"));
+ attributes.put("b", Arrays.asList("b"));
+ attributes.put("c", Arrays.asList("c"));
+
+ resource1.setAttributes(attributes);
+
+ resource1.setIconUri("icon");
+ resource1.setUris(new HashSet<>(Arrays.asList("/a", "/b", "/c")));
+
+ ResourceRepresentation resource = doCreateResource(resource1);
+ getAuthzClient();
+ List representations = authzClient.protection().resource().find(resource.getId(), null, null, null, null, null, false, true,null, null);
+
+ Assertions.assertEquals(1, representations.size());
+ Assertions.assertEquals(resource.getId(), representations.get(0).getId());
+ Assertions.assertEquals(resource.getName(), representations.get(0).getName());
+ Assertions.assertEquals(resource.getIconUri(), representations.get(0).getIconUri());
+ MatcherAssert.assertThat(resource.getUris(), Matchers.containsInAnyOrder(representations.get(0).getUris().toArray()));
+ MatcherAssert.assertThat(resource.getAttributes().entrySet(), Matchers.containsInAnyOrder(representations.get(0).getAttributes().entrySet().toArray()));
+ }
+
+ @Override
+ protected ResourceRepresentation doCreateResource(ResourceRepresentation newResource) {
+ ResourceRepresentation resource = toResourceRepresentation(newResource);
+
+ getAuthzClient();
+ ResourceRepresentation response = authzClient.protection().resource().create(resource);
+
+ return toResourceRepresentation(authzClient, response.getId());
+ }
+
+ @Override
+ protected ResourceRepresentation doUpdateResource(ResourceRepresentation resource) {
+ getAuthzClient();
+
+ authzClient.protection().resource().update(toResourceRepresentation(resource));
+
+ return toResourceRepresentation(authzClient, resource.getId());
+ }
+
+ @Override
+ protected void doRemoveResource(ResourceRepresentation resource) {
+ getAuthzClient().protection().resource().delete(resource.getId());
+ }
+
+ private ResourceRepresentation toResourceRepresentation(AuthzClient authzClient, String id) {
+ ResourceRepresentation created = authzClient.protection().resource().findById(id);
+ ResourceRepresentation resourceRepresentation = new ResourceRepresentation();
+
+ resourceRepresentation.setId(created.getId());
+ resourceRepresentation.setName(created.getName());
+ resourceRepresentation.setIconUri(created.getIconUri());
+ resourceRepresentation.setUris(created.getUris());
+ resourceRepresentation.setType(created.getType());
+ resourceRepresentation.setOwner(created.getOwner());
+ resourceRepresentation.setScopes(created.getScopes().stream().map(scopeRepresentation -> {
+ ScopeRepresentation scope = new ScopeRepresentation();
+
+ scope.setId(scopeRepresentation.getId());
+ scope.setName(scopeRepresentation.getName());
+ scope.setIconUri(scopeRepresentation.getIconUri());
+
+ return scope;
+ }).collect(Collectors.toSet()));
+
+ resourceRepresentation.setAttributes(created.getAttributes());
+
+ return resourceRepresentation;
+ }
+
+ private ResourceRepresentation toResourceRepresentation(ResourceRepresentation newResource) {
+ ResourceRepresentation resource = new ResourceRepresentation();
+
+ resource.setId(newResource.getId());
+ resource.setName(newResource.getName());
+ resource.setIconUri(newResource.getIconUri());
+
+ if (newResource.getUris() != null && !newResource.getUris().isEmpty()) {
+ resource.setUris(newResource.getUris());
+ } else {
+ resource.setUri(newResource.getUri());
+ }
+
+ resource.setType(newResource.getType());
+
+ if (newResource.getOwner() != null) {
+ resource.setOwner(newResource.getOwner().getId());
+ }
+
+ resource.setScopes(newResource.getScopes().stream().map(scopeRepresentation -> {
+ ScopeRepresentation scope = new ScopeRepresentation();
+
+ scope.setName(scopeRepresentation.getName());
+ scope.setIconUri(scopeRepresentation.getIconUri());
+
+ return scope;
+ }).collect(Collectors.toSet()));
+
+ resource.setAttributes(newResource.getAttributes());
+
+
+ return resource;
+ }
+
+ private AuthzClient getAuthzClient() {
+ if (authzClient == null) {
+ authzClient = getAuthzClient("/authorization-test/default-keycloak.json");
+ }
+
+ return authzClient;
+ }
+}
\ No newline at end of file
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourcePermissionManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourcePermissionManagementTest.java
new file mode 100644
index 0000000..1135f53
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourcePermissionManagementTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.Collections;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ResourcePermissionResource;
+import org.keycloak.admin.client.resource.ResourcePermissionsResource;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+
+/**
+ * @author Pedro Igor
+ */
+public class ResourcePermissionManagementTest extends AbstractPolicyManagementTest {
+
+ @Test
+ public void testCreateResourcePermission() {
+ AuthorizationResource authorization = getClient().authorization();
+ ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
+
+ representation.setName("Resource A Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addResource("Resource A");
+ representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testCreateResourceType() {
+ AuthorizationResource authorization = getClient().authorization();
+ ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
+
+ representation.setName("Resource A Type Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.setResourceType("test-resource");
+ representation.addPolicy("Only Marta Policy");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
+
+ representation.setName("Update Test Resource Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addResource("Resource A");
+ representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.getResources().remove("Resource A");
+ representation.addResource("Resource B");
+ representation.getPolicies().remove("Only Marta Policy");
+
+ ResourcePermissionsResource permissions = authorization.permissions().resource();
+ ResourcePermissionResource permission = permissions.findById(representation.getId());
+
+ permission.update(representation);
+
+ assertRepresentation(representation, permission);
+
+ representation.getResources().clear();
+ representation.setResourceType("changed");
+
+ permission.update(representation);
+
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
+
+ representation.setName("Test Delete Permission");
+ representation.setResourceType("test-resource");
+ representation.addPolicy("Only Marta Policy");
+
+ ResourcePermissionsResource permissions = authorization.permissions().resource();
+
+ try (Response response = permissions.create(representation)) {
+ ResourcePermissionRepresentation created = response.readEntity(ResourcePermissionRepresentation.class);
+
+ permissions.findById(created.getId()).remove();
+
+ ResourcePermissionResource removed = permissions.findById(created.getId());
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class, () -> removed.toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+ }
+
+ @Test
+ public void failCreateWithSameName() {
+ AuthorizationResource authorization = getClient().authorization();
+ ResourcePermissionRepresentation permission1 = new ResourcePermissionRepresentation();
+
+ permission1.setName("Conflicting Name Permission");
+ permission1.setResourceType("test-resource");
+ permission1.addPolicy("Only Marta Policy");
+
+ ResourcePermissionsResource permissions = authorization.permissions().resource();
+
+ permissions.create(permission1).close();
+
+ ResourcePermissionRepresentation permission2 = new ResourcePermissionRepresentation();
+
+ permission2.setName(permission1.getName());
+
+ try (Response response = permissions.create(permission2)) {
+ Assertions.assertEquals(Response.Status.CONFLICT.getStatusCode(), response.getStatus());
+ }
+ }
+
+ private void assertCreated(AuthorizationResource authorization, ResourcePermissionRepresentation representation) {
+ ResourcePermissionsResource permissions = authorization.permissions().resource();
+ try (Response response = permissions.create(representation)) {
+ ResourcePermissionRepresentation created = response.readEntity(ResourcePermissionRepresentation.class);
+ ResourcePermissionResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+ }
+
+ private void assertRepresentation(ResourcePermissionRepresentation representation, ResourcePermissionResource permission) {
+ assertRepresentation(representation, permission.toRepresentation(), () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourceServerManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourceServerManagementTest.java
new file mode 100644
index 0000000..f0005b2
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ResourceServerManagementTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2017 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.List;
+import java.util.Objects;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.client.testsuite.framework.KeycloakVersion;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
+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.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.KeycloakModelUtils;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ *
+ * @author Pedro Igor
+ */
+public class ResourceServerManagementTest extends AbstractAuthorizationTest {
+
+ @Test
+ public void testCreateAndDeleteResourceServer() throws Exception {
+ ClientsResource clientsResource = testRealmResource().clients();
+
+ try (Response response = clientsResource.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/client-with-authz-settings.json"), ClientRepresentation.class))) {
+ Assertions.assertEquals(201, response.getStatus());
+ }
+
+ List clients = clientsResource.findByClientId("authz-client");
+
+ Assertions.assertFalse(clients.isEmpty());
+
+ String clientId = clients.get(0).getId();
+ AuthorizationResource settings = clientsResource.get(clientId).authorization();
+
+ Assertions.assertEquals(PolicyEnforcementMode.PERMISSIVE, settings.exportSettings().getPolicyEnforcementMode());
+ Assertions.assertEquals(DecisionStrategy.UNANIMOUS, settings.exportSettings().getDecisionStrategy());
+
+ Assertions.assertFalse(settings.resources().findByName("Resource 1").isEmpty());
+ Assertions.assertFalse(settings.resources().findByName("Resource 15").isEmpty());
+ Assertions.assertFalse(settings.resources().findByName("Resource 20").isEmpty());
+
+ Assertions.assertNotNull(settings.permissions().resource().findByName("Resource 15 Permission"));
+ Assertions.assertNotNull(settings.policies().role().findByName("Resource 1 Policy"));
+
+ clientsResource.get(clientId).remove();
+
+ clients = clientsResource.findByClientId("authz-client");
+
+ Assertions.assertTrue(clients.isEmpty());
+ }
+
+ @Test
+ public void testInvalidRequestWhenCallingAuthzEndpoints() throws Exception {
+ ClientsResource clientsResource = testRealmResource().clients();
+ ClientRepresentation clientRepresentation = JsonSerialization.readValue(
+ getClass().getResourceAsStream("/authorization-test/client-with-authz-settings.json"),
+ ClientRepresentation.class);
+
+ clientRepresentation.setAuthorizationServicesEnabled(false);
+ clientRepresentation.setAuthorizationSettings(null);
+
+ try (Response response = clientsResource.create(clientRepresentation)) {
+ Assertions.assertEquals(201, response.getStatus());
+ }
+
+ List clients = clientsResource.findByClientId("authz-client");
+
+ Assertions.assertFalse(clients.isEmpty());
+
+ String clientId = clients.get(0).getId();
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class,
+ () -> clientsResource.get(clientId).authorization().getSettings());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+
+ @Test
+ @KeycloakVersion(min = "25.0")
+ public void testImportSettingsToDifferentClient() throws Exception {
+ ClientsResource clientsResource = testRealmResource().clients();
+ ClientRepresentation clientRep = JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/client-with-authz-settings.json"), ClientRepresentation.class);
+ clientRep.setClientId(KeycloakModelUtils.generateId());
+ try (Response response = clientsResource.create(clientRep)) {
+ Assertions.assertEquals(201, response.getStatus());
+ }
+ List clients = clientsResource.findByClientId(clientRep.getClientId());
+ Assertions.assertFalse(clients.isEmpty());
+ String clientId = clients.get(0).getId();
+ AuthorizationResource authorization = clientsResource.get(clientId).authorization();
+ ResourceServerRepresentation settings = authorization.exportSettings();
+ Assertions.assertEquals(PolicyEnforcementMode.PERMISSIVE, settings.getPolicyEnforcementMode());
+ Assertions.assertEquals(DecisionStrategy.UNANIMOUS, settings.getDecisionStrategy());
+ Assertions.assertFalse(authorization.resources().findByName("Resource 1").isEmpty());
+ Assertions.assertFalse(authorization.resources().findByName("Resource 15").isEmpty());
+ Assertions.assertFalse(authorization.resources().findByName("Resource 20").isEmpty());
+ Assertions.assertNotNull(authorization.permissions().resource().findByName("Resource 15 Permission"));
+ Assertions.assertNotNull(authorization.policies().role().findByName("Resource 1 Policy"));
+ settings.getPolicies().removeIf(p -> "js".equals(p.getType()));
+
+ ClientRepresentation anotherClientRep = ClientBuilder.create().clientId(KeycloakModelUtils.generateId()).secret("secret").authorizationServicesEnabled(true).serviceAccount().enabled(true).build();
+ clientsResource.create(anotherClientRep).close();
+ clients = clientsResource.findByClientId(anotherClientRep.getClientId());
+ Assertions.assertFalse(clients.isEmpty());
+ ClientRepresentation anotherClient = clients.get(0);
+ authorization = clientsResource.get(anotherClient.getId()).authorization();
+ authorization.importSettings(settings);
+ ResourceServerRepresentation anotherSettings = authorization.exportSettings();
+ Assertions.assertEquals(PolicyEnforcementMode.PERMISSIVE, anotherSettings.getPolicyEnforcementMode());
+ Assertions.assertEquals(DecisionStrategy.UNANIMOUS, anotherSettings.getDecisionStrategy());
+ Assertions.assertFalse(authorization.resources().findByName("Resource 1").isEmpty());
+ Assertions.assertFalse(authorization.resources().findByName("Resource 15").isEmpty());
+ Assertions.assertFalse(authorization.resources().findByName("Resource 20").isEmpty());
+ Assertions.assertNotNull(authorization.permissions().resource().findByName("Resource 15 Permission"));
+ Assertions.assertNotNull(authorization.policies().role().findByName("Resource 1 Policy"));
+ }
+
+ @Test
+ @KeycloakVersion(min = "25.0")
+ public void testExportSettings() throws Exception {
+ ClientsResource clientsResource = testRealmResource().clients();
+ ClientRepresentation clientRep = JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/client-with-authz-settings.json"), ClientRepresentation.class);
+ clientRep.setClientId(KeycloakModelUtils.generateId());
+ try (Response response = clientsResource.create(clientRep)) {
+ Assertions.assertEquals(201, response.getStatus());
+ }
+ List clients = clientsResource.findByClientId(clientRep.getClientId());
+ Assertions.assertFalse(clients.isEmpty());
+ String clientId = clients.get(0).getId();
+ AuthorizationResource authorization = clientsResource.get(clientId).authorization();
+ ResourceServerRepresentation settings = authorization.exportSettings();
+ Assertions.assertFalse(settings.getResources().stream().map(ResourceRepresentation::getId).anyMatch(Objects::nonNull));
+ Assertions.assertFalse(settings.getScopes().stream().map(ScopeRepresentation::getId).anyMatch(Objects::nonNull));
+ Assertions.assertFalse(settings.getPolicies().stream().map(PolicyRepresentation::getId).anyMatch(Objects::nonNull));
+ }
+}
\ No newline at end of file
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/RolePolicyManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/RolePolicyManagementTest.java
new file mode 100644
index 0000000..27f666a
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/RolePolicyManagementTest.java
@@ -0,0 +1,265 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.PolicyResource;
+import org.keycloak.admin.client.resource.RolePoliciesResource;
+import org.keycloak.admin.client.resource.RolePolicyResource;
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.client.testsuite.framework.KeycloakVersion;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.testsuite.util.KeycloakModelUtils;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.RoleBuilder;
+import org.keycloak.testsuite.util.RolesBuilder;
+
+/**
+ * @author Pedro Igor
+ */
+public class RolePolicyManagementTest extends AbstractPolicyManagementTest {
+
+ @Override
+ protected RealmBuilder createTestRealm() {
+ return super.createTestRealm().roles(
+ RolesBuilder.create()
+ .realmRole(new RoleRepresentation("Role A", "Role A description", false))
+ .realmRole(new RoleRepresentation("Role B", "Role B description", false))
+ .realmRole(new RoleRepresentation("Role C", "Role C description", false))
+ );
+ }
+
+ @Test
+ public void testCreateRealmRolePolicy() {
+ AuthorizationResource authorization = getClient().authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName("Realm Role Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addRole("Role A", false);
+ representation.addRole("Role B", true);
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ @KeycloakVersion(min = "25.0")
+ public void testCreateFetchRoles() {
+ AuthorizationResource authorization = getClient().authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName(KeycloakModelUtils.generateId());
+ representation.setFetchRoles(true);
+ representation.addRole("Role A", false);
+ representation.addRole("Role B", true);
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testCreateClientRolePolicy() {
+ ClientResource client = getClient();
+ AuthorizationResource authorization = client.authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName("Realm Client Role Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+
+ RolesResource roles = client.roles();
+
+ roles.create(new RoleRepresentation("Client Role A", "desc", false));
+
+ ClientRepresentation clientRep = client.toRepresentation();
+
+ roles.create(new RoleRepresentation("Client Role B", "desc", false));
+
+ representation.addRole("resource-server-test/Client Role A");
+ representation.addClientRole(clientRep.getClientId(), "Client Role B", true);
+
+ assertCreated(authorization, representation);
+ }
+
+ private void testUpdate(Boolean fetchRoles) {
+ AuthorizationResource authorization = getClient().authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName(KeycloakModelUtils.generateId());
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addRole("Role A", false);
+ representation.addRole("Role B", true);
+ representation.addRole("Role C", false);
+
+ assertCreated(authorization, representation);
+
+ representation.setName(KeycloakModelUtils.generateId());
+ representation.setDescription("changed");
+ representation.setFetchRoles(fetchRoles);
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.setRoles(representation.getRoles().stream().filter(roleDefinition -> !roleDefinition.getId().equals("Resource A")).collect(Collectors.toSet()));
+
+ RolePoliciesResource policies = authorization.policies().role();
+ RolePolicyResource permission = policies.findById(representation.getId());
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+
+ for (RolePolicyRepresentation.RoleDefinition roleDefinition : representation.getRoles()) {
+ if (roleDefinition.getId().equals("Role B")) {
+ roleDefinition.setRequired(false);
+ }
+ if (roleDefinition.getId().equals("Role C")) {
+ roleDefinition.setRequired(true);
+ }
+ }
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testUpdate() {
+ testUpdate(null);
+ }
+
+ @Test
+ @KeycloakVersion(min = "25.0")
+ public void testUpdateFetchRoles() {
+ testUpdate(true);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName("Test Delete Permission");
+ representation.addRole("Role A", false);
+
+ RolePoliciesResource policies = authorization.policies().role();
+
+ try (Response response = policies.create(representation)) {
+ RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
+
+ policies.findById(created.getId()).remove();
+
+ RolePolicyResource removed = policies.findById(created.getId());
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class, () -> removed.toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+ }
+
+ @Test
+ @KeycloakVersion(min = "25.0")
+ public void testDeleteRole() {
+ RoleRepresentation role = RoleBuilder.create().name(KeycloakModelUtils.generateId()).build();
+ getRealm().roles().create(role);
+ AuthorizationResource authorization = getClient().authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName(KeycloakModelUtils.generateId());
+ representation.addRole(role.getName(), false);
+
+ RolePoliciesResource policies = authorization.policies().role();
+
+ try (Response response = policies.create(representation)) {
+ RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
+ RolePolicyResource rolePolicy = policies.findById(created.getId());
+ RolePolicyRepresentation rolePolicyRep = rolePolicy.toRepresentation();
+ Assertions.assertEquals(1, rolePolicyRep.getRoles().size());
+
+ getRealm().roles().deleteRole(role.getName());
+ rolePolicyRep = rolePolicy.toRepresentation();
+ Assertions.assertTrue(rolePolicyRep.getRoles().isEmpty());
+ }
+ }
+
+ @Test
+ public void testGenericConfig() {
+ AuthorizationResource authorization = getClient().authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName("Test Generic Config Permission");
+ representation.addRole("Role A", false);
+
+ RolePoliciesResource policies = authorization.policies().role();
+
+ try (Response response = policies.create(representation)) {
+ RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
+
+ PolicyResource policy = authorization.policies().policy(created.getId());
+ PolicyRepresentation genericConfig = policy.toRepresentation();
+
+ Assertions.assertNotNull(genericConfig.getConfig());
+ Assertions.assertNotNull(genericConfig.getConfig().get("roles"));
+
+ RoleRepresentation role = getRealm().roles().get("Role A").toRepresentation();
+
+ Assertions.assertTrue(genericConfig.getConfig().get("roles").contains(role.getId()));
+ }
+ }
+
+ private void assertCreated(AuthorizationResource authorization, RolePolicyRepresentation representation) {
+ RolePoliciesResource permissions = authorization.policies().role();
+
+ try (Response response = permissions.create(representation)) {
+ RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
+ RolePolicyResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+ }
+
+ private void assertRepresentation(RolePolicyRepresentation representation, RolePolicyResource permission) {
+ RolePolicyRepresentation actual = permission.toRepresentation();
+ assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+ Assertions.assertEquals(representation.getRoles().size(), actual.getRoles().size());
+ ClientRepresentation clientRep = getClient().toRepresentation();
+ Assertions.assertEquals(0, actual.getRoles().stream().filter(actualDefinition -> !representation.getRoles().stream()
+ .filter(roleDefinition ->
+ (getRoleName(actualDefinition.getId()).equals(roleDefinition.getId())|| (clientRep.getClientId() + "/" + getRoleName(actualDefinition.getId())).equals(roleDefinition.getId()))
+ && Objects.equals(actualDefinition.isRequired(), roleDefinition.isRequired()))
+ .findFirst().isPresent())
+ .count());
+ if (representation.isFetchRoles() != null) {
+ Assertions.assertEquals(representation.isFetchRoles(), actual.isFetchRoles());
+ }
+ }
+
+ private String getRoleName(String id) {
+ return getRealm().rolesById().getRole(id).getName();
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ScopeManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ScopeManagementTest.java
new file mode 100644
index 0000000..b68150c
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ScopeManagementTest.java
@@ -0,0 +1,132 @@
+/*
+ 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.List;
+import java.util.UUID;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.ResourceScopeResource;
+import org.keycloak.admin.client.resource.ResourcesResource;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+
+/**
+ *
+ * @author Pedro Igor
+ */
+public class ScopeManagementTest extends AbstractAuthorizationTest {
+
+ @Test
+ public void testCreate() {
+ ScopeRepresentation newScope = createDefaultScope().toRepresentation();
+
+ Assertions.assertEquals("Test Scope", newScope.getName());
+ Assertions.assertEquals("Scope Icon", newScope.getIconUri());
+ }
+
+ @Test
+ public void testUpdate() {
+ ResourceScopeResource scopeResource = createDefaultScope();
+ ScopeRepresentation scope = scopeResource.toRepresentation();
+
+ scope.setName("changed");
+ scope.setIconUri("changed");
+
+ scopeResource.update(scope);
+
+ scope = scopeResource.toRepresentation();
+
+ Assertions.assertEquals("changed", scope.getName());
+ Assertions.assertEquals("changed", scope.getIconUri());
+ }
+
+ @Test
+ public void testNotUpdateOnResourceUpdate() {
+ ResourceScopeResource scopeResource = createDefaultScope();
+ ScopeRepresentation scope = scopeResource.toRepresentation();
+
+ scope.setName("changed");
+ scope.setDisplayName("changed");
+ scope.setIconUri("changed");
+
+ scopeResource.update(scope);
+
+ scope = scopeResource.toRepresentation();
+
+ Assertions.assertEquals("changed", scope.getName());
+ Assertions.assertEquals("changed", scope.getDisplayName());
+ Assertions.assertEquals("changed", scope.getIconUri());
+
+ ResourcesResource resources = getClientResource().authorization().resources();
+ ResourceRepresentation resource;
+
+ try (Response response = resources
+ .create(new ResourceRepresentation(UUID.randomUUID().toString(), scope.getName()))) {
+ resource = response.readEntity(ResourceRepresentation.class);
+ }
+
+ resource.getScopes().iterator().next().setDisplayName(null);
+ resources.resource(resource.getId()).update(resource);
+
+ scope = scopeResource.toRepresentation();
+
+ Assertions.assertEquals("changed", scope.getName());
+ Assertions.assertEquals("changed", scope.getDisplayName());
+ Assertions.assertEquals("changed", scope.getIconUri());
+ }
+
+ @Test
+ public void testDelete() {
+ ResourceScopeResource scopeResource = createDefaultScope();
+
+ scopeResource.remove();
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class, () -> scopeResource.toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+
+ @Test
+ public void testDeleteAndPolicyUpdate() {
+ ResourceScopeResource scopeResource = createDefaultScope();
+
+ ScopeRepresentation scopeRepresentation = scopeResource.toRepresentation();
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName(scopeRepresentation.getName());
+ representation.addScope(scopeRepresentation.getId());
+
+ getClientResource().authorization().permissions().scope().create(representation);
+
+ ScopePermissionRepresentation permissionRepresentation = getClientResource().authorization().permissions().scope()
+ .findByName(scopeRepresentation.getName());
+ List scopes = getClientResource().authorization().policies()
+ .policy(permissionRepresentation.getId()).scopes();
+
+ Assertions.assertEquals(1, scopes.size());
+
+ scopeResource.remove();
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class,
+ () -> getClientResource().authorization().policies().policy(permissionRepresentation.getId()).scopes());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ScopePermissionManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ScopePermissionManagementTest.java
new file mode 100644
index 0000000..fe03461
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/ScopePermissionManagementTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ScopePermissionResource;
+import org.keycloak.admin.client.resource.ScopePermissionsResource;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+
+/**
+ * @author Pedro Igor
+ */
+public class ScopePermissionManagementTest extends AbstractPolicyManagementTest {
+
+ @Test
+ public void testCreateResourceScopePermission() {
+ AuthorizationResource authorization = getClient().authorization();
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName("Resource A Scope Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addResource("Resource A");
+ representation.addScope("read", "execute");
+ representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testCreateScopePermission() {
+ AuthorizationResource authorization = getClient().authorization();
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName("Read Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addScope("read", "write");
+ representation.addPolicy("Only Marta Policy");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName("Update Test Scope Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addResource("Resource A");
+ representation.addScope("read", "execute");
+ representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.getResources().remove("Resource A");
+ representation.addResource("Resource B");
+ representation.getScopes().remove("execute");
+ representation.getPolicies().remove("Only Marta Policy");
+
+ ScopePermissionsResource permissions = authorization.permissions().scope();
+ ScopePermissionResource permission = permissions.findById(representation.getId());
+
+ permission.update(representation);
+
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName("Test Delete Permission");
+ representation.addScope("execute");
+ representation.addPolicy("Only Marta Policy");
+
+ assertCreated(authorization, representation);
+
+ ScopePermissionsResource permissions = authorization.permissions().scope();
+
+ permissions.findById(representation.getId()).remove();
+
+ ScopePermissionResource removed = permissions.findById(representation.getId());
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class, () -> removed.toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+
+ @Test
+ public void failCreateWithSameName() {
+ AuthorizationResource authorization = getClient().authorization();
+ ScopePermissionRepresentation permission1 = new ScopePermissionRepresentation();
+
+ permission1.setName("Conflicting Name Permission");
+ permission1.addScope("read");
+ permission1.addPolicy("Only Marta Policy");
+
+ ScopePermissionsResource permissions = authorization.permissions().scope();
+
+ permissions.create(permission1).close();
+
+ ScopePermissionRepresentation permission2 = new ScopePermissionRepresentation();
+
+ permission2.setName(permission1.getName());
+
+ try (Response response = permissions.create(permission2)) {
+ Assertions.assertEquals(Response.Status.CONFLICT.getStatusCode(), response.getStatus());
+ }
+ }
+
+ private void assertCreated(AuthorizationResource authorization, ScopePermissionRepresentation representation) {
+ ScopePermissionsResource permissions = authorization.permissions().scope();
+
+ try (Response response = permissions.create(representation)) {
+ ScopePermissionRepresentation created = response.readEntity(ScopePermissionRepresentation.class);
+ ScopePermissionResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+ }
+
+ private void assertRepresentation(ScopePermissionRepresentation representation, ScopePermissionResource permission) {
+ assertRepresentation(representation, permission.toRepresentation(), () -> permission.resources(), () -> permission.scopes(), () -> permission.associatedPolicies());
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/TimePolicyManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/TimePolicyManagementTest.java
new file mode 100644
index 0000000..ca83a42
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/TimePolicyManagementTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.Collections;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.TimePoliciesResource;
+import org.keycloak.admin.client.resource.TimePolicyResource;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
+
+/**
+ * @author Pedro Igor
+ */
+public class TimePolicyManagementTest extends AbstractPolicyManagementTest {
+
+ @Test
+ public void testCreate() {
+ AuthorizationResource authorization = getClient().authorization();
+ assertCreated(authorization, createRepresentation("Time Policy"));
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ TimePolicyRepresentation representation = createRepresentation("Update Time Policy");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.setDayMonth("11");
+ representation.setDayMonthEnd("22");
+ representation.setMonth("7");
+ representation.setMonthEnd("9");
+ representation.setYear("2019");
+ representation.setYearEnd("2030");
+ representation.setHour("15");
+ representation.setHourEnd("23");
+ representation.setMinute("55");
+ representation.setMinuteEnd("58");
+ representation.setNotBefore("2019-01-01 00:00:00");
+ representation.setNotOnOrAfter("2019-02-03 00:00:00");
+
+ TimePoliciesResource policies = authorization.policies().time();
+ TimePolicyResource permission = policies.findById(representation.getId());
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+
+ representation.setDayMonth(null);
+ representation.setDayMonthEnd(null);
+ representation.setMonth(null);
+ representation.setMonthEnd(null);
+ representation.setYear(null);
+ representation.setYearEnd(null);
+ representation.setHour(null);
+ representation.setHourEnd(null);
+ representation.setMinute(null);
+ representation.setMinuteEnd(null);
+ representation.setNotBefore(null);
+ representation.setNotOnOrAfter("2019-02-03 00:00:00");
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+
+ representation.setNotOnOrAfter(null);
+ representation.setHour("2");
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ TimePolicyRepresentation representation = createRepresentation("Test Delete Policy");
+ TimePoliciesResource policies = authorization.policies().time();
+
+ try (Response response = policies.create(representation)) {
+ TimePolicyRepresentation created = response.readEntity(TimePolicyRepresentation.class);
+
+ policies.findById(created.getId()).remove();
+
+ TimePolicyResource removed = policies.findById(created.getId());
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class, () -> removed.toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+ }
+
+ private TimePolicyRepresentation createRepresentation(String name) {
+ TimePolicyRepresentation representation = new TimePolicyRepresentation();
+
+ representation.setName(name);
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.setDayMonth("1");
+ representation.setDayMonthEnd("2");
+ representation.setMonth("3");
+ representation.setMonthEnd("4");
+ representation.setYear("5");
+ representation.setYearEnd("6");
+ representation.setHour("7");
+ representation.setHourEnd("8");
+ representation.setMinute("9");
+ representation.setMinuteEnd("10");
+ representation.setNotBefore("2017-01-01 00:00:00");
+ representation.setNotOnOrAfter("2017-02-01 00:00:00");
+ return representation;
+ }
+
+ private void assertCreated(AuthorizationResource authorization, TimePolicyRepresentation representation) {
+ TimePoliciesResource permissions = authorization.policies().time();
+
+ try (Response response = permissions.create(representation)) {
+ TimePolicyRepresentation created = response.readEntity(TimePolicyRepresentation.class);
+ TimePolicyResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+ }
+
+ private void assertRepresentation(TimePolicyRepresentation representation, TimePolicyResource permission) {
+ TimePolicyRepresentation actual = permission.toRepresentation();
+ assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+ Assertions.assertEquals(representation.getDayMonth(), actual.getDayMonth());
+ Assertions.assertEquals(representation.getDayMonthEnd(), actual.getDayMonthEnd());
+ Assertions.assertEquals(representation.getMonth(), actual.getMonth());
+ Assertions.assertEquals(representation.getMonthEnd(), actual.getMonthEnd());
+ Assertions.assertEquals(representation.getYear(), actual.getYear());
+ Assertions.assertEquals(representation.getYearEnd(), actual.getYearEnd());
+ Assertions.assertEquals(representation.getHour(), actual.getHour());
+ Assertions.assertEquals(representation.getHourEnd(), actual.getHourEnd());
+ Assertions.assertEquals(representation.getMinute(), actual.getMinute());
+ Assertions.assertEquals(representation.getMinuteEnd(), actual.getMinuteEnd());
+ Assertions.assertEquals(representation.getNotBefore(), actual.getNotBefore());
+ Assertions.assertEquals(representation.getNotOnOrAfter(), actual.getNotOnOrAfter());
+ }
+}
diff --git a/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/UserPolicyManagementTest.java b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/UserPolicyManagementTest.java
new file mode 100644
index 0000000..29e8aa3
--- /dev/null
+++ b/testsuite/authz-tests/src/test/java/org/keycloak/client/testsuite/authz/admin/UserPolicyManagementTest.java
@@ -0,0 +1,256 @@
+/*
+ * 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.client.testsuite.authz.admin;
+
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.Response;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.PolicyResource;
+import org.keycloak.admin.client.resource.UserPoliciesResource;
+import org.keycloak.admin.client.resource.UserPolicyResource;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author Pedro Igor
+ */
+public class UserPolicyManagementTest extends AbstractPolicyManagementTest {
+
+ @Override
+ protected RealmBuilder createTestRealm() {
+ return super.createTestRealm()
+ .user(UserBuilder.create().username("User A"))
+ .user(UserBuilder.create().username("User B"))
+ .user(UserBuilder.create().username("User C"))
+ .user(UserBuilder.create().username("User D"))
+ .user(UserBuilder.create().username("User E"))
+ .user(UserBuilder.create().username("User F"));
+ }
+
+ @Test
+ public void testCreate() {
+ AuthorizationResource authorization = getClient().authorization();
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName("Realm User Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addUser("User A");
+ representation.addUser("User B");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName("Update Test User Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addUser("User A");
+ representation.addUser("User B");
+ representation.addUser("User C");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.setUsers(representation.getUsers().stream().filter(userName -> !userName.equals("User A")).collect(Collectors.toSet()));
+
+ UserPoliciesResource policies = authorization.policies().user();
+ UserPolicyResource permission = policies.findById(representation.getId());
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+
+ representation.setUsers(representation.getUsers().stream().filter(userName -> !userName.equals("User C")).collect(Collectors.toSet()));
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName("Test Delete Permission");
+ representation.addUser("User A");
+
+ UserPoliciesResource policies = authorization.policies().user();
+
+ try (Response response = policies.create(representation)) {
+ UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
+
+ policies.findById(created.getId()).remove();
+
+ UserPolicyResource removed = policies.findById(created.getId());
+
+ NotFoundException nfe = Assertions.assertThrows(NotFoundException.class, () -> removed.toRepresentation());
+ Assertions.assertEquals(404, nfe.getResponse().getStatus());
+ }
+ }
+
+ @Test
+ public void testDeleteUser() {
+ AuthorizationResource authorization = getClient().authorization();
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName("Realm User Policy Delete");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addUser("User D");
+ representation.addUser("User E");
+ representation.addUser("User F");
+
+ assertCreated(authorization, representation);
+
+ UsersResource users = getRealm().users();
+ UserRepresentation user = users.search("User D").get(0);
+
+ users.get(user.getId()).remove();
+
+ representation = authorization.policies().user().findById(representation.getId()).toRepresentation();
+
+ Assertions.assertEquals(2, representation.getUsers().size());
+ Assertions.assertFalse(representation.getUsers().contains(user.getId()));
+
+ user = users.search("User E").get(0);
+ users.get(user.getId()).remove();
+
+ representation = authorization.policies().user().findById(representation.getId()).toRepresentation();
+
+ Assertions.assertEquals(1, representation.getUsers().size());
+ Assertions.assertFalse(representation.getUsers().contains(user.getId()));
+
+ user = users.search("User F").get(0);
+ users.get(user.getId()).remove();
+
+ representation = authorization.policies().user().findById(representation.getId()).toRepresentation();
+
+ Assertions.assertEquals(0, representation.getUsers().size());
+ Assertions.assertFalse(representation.getUsers().contains(user.getId()));
+ }
+
+ @Test
+ public void testGenericConfig() {
+ AuthorizationResource authorization = getClient().authorization();
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName("Test Generic Config Permission");
+ representation.addUser("User A");
+
+ UserPoliciesResource policies = authorization.policies().user();
+
+ try (Response response = policies.create(representation)) {
+ UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
+
+ PolicyResource policy = authorization.policies().policy(created.getId());
+ PolicyRepresentation genericConfig = policy.toRepresentation();
+
+ Assertions.assertNotNull(genericConfig.getConfig());
+ Assertions.assertNotNull(genericConfig.getConfig().get("users"));
+
+ UserRepresentation user = getRealm().users().search("User A").get(0);
+
+ Assertions.assertTrue(genericConfig.getConfig().get("users").contains(user.getId()));
+ }
+ }
+
+ @Test
+ public void failInvalidUser() {
+ AuthorizationResource authorization = getClient().authorization();
+
+ PolicyRepresentation policy = new PolicyRepresentation();
+
+ policy.setName("User Policy-Malformed");
+ policy.setDescription("Description of a malformed user Policy");
+ policy.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ policy.setType("user");
+
+ Map config = new HashMap<>();
+
+ // here we put something invalid ... a user ID would be needed
+ config.put("users", "[\"doesnotexist\"]");
+
+ policy.setConfig(config);
+
+ try (Response response = authorization.policies().create(policy)) {
+ Assertions.assertEquals(Response.Status.INTERNAL_SERVER_ERROR, response.getStatusInfo());
+ }
+
+ config.put("users", "");
+
+ policy.setConfig(config);
+
+ try (Response response = authorization.policies().create(policy)) {
+ Assertions.assertEquals(Response.Status.INTERNAL_SERVER_ERROR, response.getStatusInfo());
+ }
+
+ config.clear();
+
+ policy.setConfig(config);
+
+ try (Response response = authorization.policies().create(policy)) {
+ Assertions.assertEquals(Response.Status.INTERNAL_SERVER_ERROR, response.getStatusInfo());
+ }
+ }
+
+ private void assertCreated(AuthorizationResource authorization, UserPolicyRepresentation representation) {
+ UserPoliciesResource permissions = authorization.policies().user();
+
+ try (Response response = permissions.create(representation)) {
+ Assertions.assertEquals(201, response.getStatus());
+ UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
+ UserPolicyResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+ }
+
+ private void assertRepresentation(UserPolicyRepresentation representation, UserPolicyResource permission) {
+ UserPolicyRepresentation actual = permission.toRepresentation();
+ assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+ Assertions.assertEquals(representation.getUsers().size(), actual.getUsers().size());
+ Assertions.assertEquals(0, actual.getUsers().stream().filter(userId -> !representation.getUsers().stream()
+ .filter(userName -> getUserName(userId).equalsIgnoreCase(userName))
+ .findFirst().isPresent())
+ .count());
+ }
+
+ private String getUserName(String id) {
+ return getRealm().users().get(id).toRepresentation().getUsername();
+ }
+}
diff --git a/testsuite/authz-tests/src/test/resources/authorization-test/client-with-authz-settings.json b/testsuite/authz-tests/src/test/resources/authorization-test/client-with-authz-settings.json
new file mode 100644
index 0000000..0c034f4
--- /dev/null
+++ b/testsuite/authz-tests/src/test/resources/authorization-test/client-with-authz-settings.json
@@ -0,0 +1,856 @@
+{
+ "clientId": "authz-client",
+ "enabled": true,
+ "publicClient": false,
+ "secret": "secret",
+ "directAccessGrantsEnabled": true,
+ "serviceAccountsEnabled": true,
+ "authorizationServicesEnabled": true,
+ "redirectUris": [
+ "http://localhost/authz-client/*"
+ ],
+ "webOrigins": [
+ "http://localhost"
+ ],
+ "authorizationSettings": {
+ "allowRemoteResourceManagement": true,
+ "policyEnforcementMode": "PERMISSIVE",
+ "resources": [
+ {
+ "name": "Default Resource",
+ "uri": "/*",
+ "type": "urn:authz-client:resources:default"
+ },
+ {
+ "name": "Resource 1",
+ "uri": "/protected/resource/1",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 2",
+ "uri": "/protected/resource/2",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 3",
+ "uri": "/protected/resource/3",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 4",
+ "uri": "/protected/resource/4",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 5",
+ "uri": "/protected/resource/5",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 6",
+ "uri": "/protected/resource/6",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 7",
+ "uri": "/protected/resource/7",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 8",
+ "uri": "/protected/resource/8",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 9",
+ "uri": "/protected/resource/9",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 10",
+ "uri": "/protected/resource/10",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 11",
+ "uri": "/protected/resource/11",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 12",
+ "uri": "/protected/resource/12",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 13",
+ "uri": "/protected/resource/13",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 14",
+ "uri": "/protected/resource/14",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 15",
+ "uri": "/protected/resource/15",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 16",
+ "uri": "/protected/resource/16",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 17",
+ "uri": "/protected/resource/17",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 18",
+ "uri": "/protected/resource/18",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 19",
+ "uri": "/protected/resource/19",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ },
+ {
+ "name": "Resource 20",
+ "uri": "/protected/resource/20",
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ }
+ ],
+ "policies": [
+ {
+ "name": "Resource 1 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 2 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 3 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 4 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 5 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 6 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 7 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 8 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 9 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 10 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 11 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 12 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 13 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 14 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 15 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 16 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 17 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 18 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 19 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Resource 20 Policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"realm-role\",\"required\":false}]"
+ }
+ },
+ {
+ "name": "Default Permission",
+ "description": "A permission that applies to the default resource type",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "defaultResourceType": "urn:authz-client:resources:default",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 1 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 1\"]",
+ "applyPolicies": "[\"Resource 1 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 2 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 2\"]",
+ "applyPolicies": "[\"Resource 2 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 3 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 3\"]",
+ "applyPolicies": "[\"Resource 3 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 4 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 4\"]",
+ "applyPolicies": "[\"Resource 4 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 5 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 5\"]",
+ "applyPolicies": "[\"Resource 5 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 6 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 6\"]",
+ "applyPolicies": "[\"Resource 6 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 7 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 7\"]",
+ "applyPolicies": "[\"Resource 7 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 8 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 8\"]",
+ "applyPolicies": "[\"Resource 8 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 9 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 9\"]",
+ "applyPolicies": "[\"Resource 9 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 10 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 10\"]",
+ "applyPolicies": "[\"Resource 10 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 11 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 11\"]",
+ "applyPolicies": "[\"Resource 11 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 12 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 12\"]",
+ "applyPolicies": "[\"Resource 12 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 13 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 13\"]",
+ "applyPolicies": "[\"Resource 13 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 14 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 14\"]",
+ "applyPolicies": "[\"Resource 14 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 15 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 15\"]",
+ "applyPolicies": "[\"Resource 15 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 16 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 16\"]",
+ "applyPolicies": "[\"Resource 16 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 17 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 17\"]",
+ "applyPolicies": "[\"Resource 17 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 18 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 18\"]",
+ "applyPolicies": "[\"Resource 18 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 19 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 19\"]",
+ "applyPolicies": "[\"Resource 19 Policy\"]"
+ }
+ },
+ {
+ "name": "Resource 20 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Resource 20\"]",
+ "applyPolicies": "[\"Resource 20 Policy\"]"
+ }
+ }
+ ],
+ "scopes": [
+ {
+ "name": "Scope B"
+ },
+ {
+ "name": "Scope A"
+ },
+ {
+ "name": "Scope D"
+ },
+ {
+ "name": "Scope C"
+ },
+ {
+ "name": "Scope E"
+ }
+ ]
+ }
+}
diff --git a/testsuite/authz-tests/src/test/resources/authorization-test/import-authorization-unordered-settings.json b/testsuite/authz-tests/src/test/resources/authorization-test/import-authorization-unordered-settings.json
new file mode 100644
index 0000000..0f7ad8c
--- /dev/null
+++ b/testsuite/authz-tests/src/test/resources/authorization-test/import-authorization-unordered-settings.json
@@ -0,0 +1,182 @@
+{
+ "allowRemoteResourceManagement": true,
+ "policyEnforcementMode": "ENFORCING",
+ "resources": [
+ {
+ "name": "User Profile Resource",
+ "uri": "/profile",
+ "type": "http://photoz.com/profile",
+ "scopes": [
+ {
+ "name": "urn:photoz.com:scopes:profile:view"
+ }
+ ]
+ },
+ {
+ "name": "Album Resource",
+ "uri": "/album/*",
+ "type": "http://photoz.com/album",
+ "scopes": [
+ {
+ "name": "urn:photoz.com:scopes:album:view"
+ },
+ {
+ "name": "urn:photoz.com:scopes:album:delete"
+ },
+ {
+ "name": "urn:photoz.com:scopes:album:create"
+ }
+ ]
+ },
+ {
+ "name": "Admin Resources",
+ "uri": "/admin/*",
+ "type": "http://photoz.com/admin",
+ "scopes": [
+ {
+ "name": "urn:photoz.com:scopes:album:admin:manage"
+ }
+ ]
+ }
+ ],
+ "policies": [
+ {
+ "name": "View User Permission",
+ "description": "Defines who is allowed to view an user profile",
+ "type": "scope",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "applyPolicies": "[\"Only From @keycloak.org or Admin\"]",
+ "scopes": "[\"urn:photoz.com:scopes:profile:view\"]"
+ }
+ },
+ {
+ "name": "Only Owner Policy",
+ "description": "Defines that only the resource owner is allowed to do something",
+ "type": "script-scripts/only-owner-policy.js",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS"
+ },
+ {
+ "name": "Any User Policy",
+ "description": "Defines that only users from well known clients are allowed to access",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"user\"},{\"id\":\"resource-server-test/manage-albums\",\"required\":true}]"
+ }
+ },
+ {
+ "name": "Only From a Specific Client Address",
+ "description": "Defines that only clients from a specific address can do something",
+ "type": "script-scripts/only-from-specific-address-policy.js",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS"
+ },
+ {
+ "name": "Administration Policy",
+ "description": "Defines that only administrators from a specific network address can do something.",
+ "type": "aggregate",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "applyPolicies": "[\"Only From a Specific Client Address\",\"Any Admin Policy\"]"
+ }
+ },
+ {
+ "name": "Only Owner and Administrators Policy",
+ "description": "Defines that only the resource owner and administrators can do something",
+ "type": "aggregate",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "applyPolicies": "[\"Administration Policy\",\"Only Owner Policy\"]"
+ }
+ },
+ {
+ "name": "Album Resource Permission",
+ "description": "General policies that apply to all album resources.",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "defaultResourceType": "http://photoz.com/album",
+ "default": "true",
+ "applyPolicies": "[\"Any User Policy\",\"Administration Policy\"]"
+ }
+ },
+ {
+ "name": "Admin Resource Permission",
+ "description": "General policy for any administrative resource.",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "defaultResourceType": "http://photoz.com/admin",
+ "default": "true",
+ "applyPolicies": "[\"Administration Policy\"]"
+ }
+ },
+ {
+ "name": "Delete Album Permission",
+ "description": "A policy that only allows the owner to delete his albums.",
+ "type": "scope",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "applyPolicies": "[\"Only Owner and Administrators Policy\"]",
+ "scopes": "[\"urn:photoz.com:scopes:album:delete\"]"
+ }
+ },
+ {
+ "name": "Any Admin Policy",
+ "description": "Defines that adminsitrators can do something",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"resource-server-test/admin\",\"required\":true}]"
+ }
+ },
+ {
+ "name": "Only From @keycloak.org or Admin",
+ "description": "Defines that only users from @keycloak.org",
+ "type": "script-scripts/only-from-specific-domain-or-admin-policy.js",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS"
+ },
+ {
+ "name": "Test Client Policy",
+ "type": "client",
+ "config": {
+ "clients": "[\"admin-cli\"]"
+ }
+ },
+ {
+ "name": "Test User Policy",
+ "type": "user",
+ "config": {
+ "users": "[\"alice\"]"
+ }
+ }
+ ],
+ "scopes": [
+ {
+ "name": "urn:photoz.com:scopes:profile:view"
+ },
+ {
+ "name": "urn:photoz.com:scopes:album:view"
+ },
+ {
+ "name": "urn:photoz.com:scopes:album:create"
+ },
+ {
+ "name": "urn:photoz.com:scopes:album:delete"
+ },
+ {
+ "name": "urn:photoz.com:scopes:album:admin:manage"
+ }
+ ]
+}
diff --git a/testsuite/framework/src/main/java/org/keycloak/testsuite/util/RealmBuilder.java b/testsuite/framework/src/main/java/org/keycloak/testsuite/util/RealmBuilder.java
index 72116b2..4f142f5 100644
--- a/testsuite/framework/src/main/java/org/keycloak/testsuite/util/RealmBuilder.java
+++ b/testsuite/framework/src/main/java/org/keycloak/testsuite/util/RealmBuilder.java
@@ -85,6 +85,11 @@ public RealmBuilder roles(RolesRepresentation roles) {
return this;
}
+ public RealmBuilder adminEvents() {
+ rep.setAdminEventsEnabled(true);
+ return this;
+ }
+
public RealmBuilder events() {
return events(new EventType[0]);
}