diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java index 456bc01759..6b4f368c7f 100644 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java @@ -63,6 +63,9 @@ public interface RealmResource { @Path("roles") RolesResource roles(); + @Path("roles-by-id") + RoleByIdResource rolesById(); + @Path("groups") GroupsResource groups(); diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleByIdResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleByIdResource.java new file mode 100755 index 0000000000..e837f5ff58 --- /dev/null +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleByIdResource.java @@ -0,0 +1,81 @@ +/* + * 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.admin.client.resource; + +import org.keycloak.representations.idm.RoleRepresentation; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import java.util.List; +import java.util.Set; + +/** + * Sometimes its easier to just interact with roles by their ID instead of container/role-name + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface RoleByIdResource { + + @Path("{role-id}") + @GET + @Produces(MediaType.APPLICATION_JSON) + RoleRepresentation getRole(final @PathParam("role-id") String id); + + @Path("{role-id}") + @DELETE + void deleteRole(final @PathParam("role-id") String id); + + @Path("{role-id}") + @PUT + @Consumes(MediaType.APPLICATION_JSON) + void updateRole(final @PathParam("role-id") String id, RoleRepresentation rep); + + @Path("{role-id}/composites") + @POST + @Consumes(MediaType.APPLICATION_JSON) + void addComposites(final @PathParam("role-id") String id, List roles); + + @Path("{role-id}/composites") + @GET + @Produces(MediaType.APPLICATION_JSON) + Set getRoleComposites(@PathParam("role-id") String id); + + @Path("{role-id}/composites/realm") + @GET + @Produces(MediaType.APPLICATION_JSON) + Set getRealmRoleComposites(@PathParam("role-id") String id); + + @Path("{role-id}/composites/clients/{client}") + @GET + @Produces(MediaType.APPLICATION_JSON) + Set getClientRoleComposites(@PathParam("role-id") String id, @PathParam("client") String client); + + @Path("{role-id}/composites") + @DELETE + @Consumes(MediaType.APPLICATION_JSON) + void deleteComposites(final @PathParam("role-id") String id, List roles); + +} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java index 26b1a4a7dd..58ccb6aac2 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java @@ -213,30 +213,6 @@ public class RoleByIdResource extends RoleResource { return getClientRoleComposites(clientModel, role); } - /** - * Get client-level roles for the client that are in the role's composite - * - * @param role - * @param client - * @return - */ - @Path("{role-id}/composites/clients/{client}") - @GET - @NoCache - @Produces(MediaType.APPLICATION_JSON) - public Set getClientByIdRoleComposites(final @PathParam("role-id") String role, - final @PathParam("client") String client) { - auth.requireAny(); - - RoleModel roleModel = getRoleModel(role); - ClientModel clientModel = realm.getClientById(client); - if (clientModel == null) { - throw new NotFoundException("Could not find client"); - - } - return getClientRoleComposites(clientModel, roleModel); - } - /** * Remove a set of roles from the role's composite * diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java index ddd2f53bbb..770dffa592 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java @@ -20,11 +20,14 @@ package org.keycloak.testsuite; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.LinkedList; import java.util.List; +import java.util.Set; import static org.junit.Assert.assertArrayEquals; @@ -33,6 +36,12 @@ import static org.junit.Assert.assertArrayEquals; */ public class Assert extends org.junit.Assert { + public static void assertNames(Set actual, String... expected) { + Arrays.sort(expected); + String[] actualNames = names(new LinkedList(actual)); + assertArrayEquals("Expected: " + Arrays.toString(expected) + ", was: " + Arrays.toString(actualNames), expected, actualNames); + } + public static void assertNames(List actual, String... expected) { Arrays.sort(expected); String[] actualNames = names(actual); @@ -65,6 +74,8 @@ public class Assert extends org.junit.Assert { return ((ClientRepresentation) o1).getClientId(); } else if (o1 instanceof IdentityProviderRepresentation) { return ((IdentityProviderRepresentation) o1).getAlias(); + } else if (o1 instanceof RoleRepresentation) { + return ((RoleRepresentation) o1).getName(); } throw new IllegalArgumentException(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java new file mode 100644 index 0000000000..c2303ef32c --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java @@ -0,0 +1,133 @@ +/* + * 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.testsuite.admin; + +import org.junit.Before; +import org.junit.Test; +import org.keycloak.admin.client.resource.RoleByIdResource; +import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.util.ClientBuilder; +import org.keycloak.testsuite.util.RoleBuilder; + +import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.Response; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.*; + +/** + * @author Stian Thorgersen + */ +public class RoleByIdResourceTest extends AbstractAdminTest { + + private RoleByIdResource resource; + + private Map ids = new HashMap<>(); + private String clientUuid; + + @Before + public void before() { + adminClient.realm(REALM_NAME).roles().create(RoleBuilder.create().name("role-a").description("Role A").build()); + adminClient.realm(REALM_NAME).roles().create(RoleBuilder.create().name("role-b").description("Role B").build()); + + Response response = adminClient.realm(REALM_NAME).clients().create(ClientBuilder.create().clientId("client-a").build()); + clientUuid = ApiUtil.getCreatedId(response); + adminClient.realm(REALM_NAME).clients().get(clientUuid).roles().create(RoleBuilder.create().name("role-c").description("Role C").build()); + + for (RoleRepresentation r : adminClient.realm(REALM_NAME).roles().list()) { + ids.put(r.getName(), r.getId()); + } + + for (RoleRepresentation r : adminClient.realm(REALM_NAME).clients().get(clientUuid).roles().list()) { + ids.put(r.getName(), r.getId()); + } + + resource = adminClient.realm(REALM_NAME).rolesById(); + } + + @Test + public void getRole() { + RoleRepresentation role = resource.getRole(ids.get("role-a")); + assertNotNull(role); + assertEquals("role-a", role.getName()); + assertEquals("Role A", role.getDescription()); + assertFalse(role.isComposite()); + } + + @Test + public void updateRole() { + RoleRepresentation role = resource.getRole(ids.get("role-a")); + + role.setName("role-a-new"); + role.setDescription("Role A New"); + + resource.updateRole(ids.get("role-a"), role); + + role = resource.getRole(ids.get("role-a")); + + assertNotNull(role); + assertEquals("role-a-new", role.getName()); + assertEquals("Role A New", role.getDescription()); + assertFalse(role.isComposite()); + } + + @Test + public void deleteRole() { + assertNotNull(resource.getRole(ids.get("role-a"))); + resource.deleteRole(ids.get("role-a")); + try { + resource.getRole(ids.get("role-a")); + fail("Expected 404"); + } catch (NotFoundException e) { + } + } + + @Test + public void composites() { + assertFalse(resource.getRole(ids.get("role-a")).isComposite()); + assertEquals(0, resource.getRoleComposites(ids.get("role-a")).size()); + + List l = new LinkedList<>(); + l.add(RoleBuilder.create().id(ids.get("role-b")).build()); + l.add(RoleBuilder.create().id(ids.get("role-c")).build()); + resource.addComposites(ids.get("role-a"), l); + + Set composites = resource.getRoleComposites(ids.get("role-a")); + + assertTrue(resource.getRole(ids.get("role-a")).isComposite()); + Assert.assertNames(composites, "role-b", "role-c"); + + Set realmComposites = resource.getRealmRoleComposites(ids.get("role-a")); + Assert.assertNames(realmComposites, "role-b"); + + Set clientComposites = resource.getClientRoleComposites(ids.get("role-a"), clientUuid); + Assert.assertNames(clientComposites, "role-c"); + + resource.deleteComposites(ids.get("role-a"), l); + + assertFalse(resource.getRole(ids.get("role-a")).isComposite()); + assertEquals(0, resource.getRoleComposites(ids.get("role-a")).size()); + + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RoleBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RoleBuilder.java index d46d1602b6..1cf46dfd3c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RoleBuilder.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RoleBuilder.java @@ -43,6 +43,11 @@ public class RoleBuilder { return this; } + public RoleBuilder description(String description) { + rep.setDescription(description); + return this; + } + public RoleRepresentation build() { return rep; }