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]); }