/* * 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.client; import org.junit.Assert; import org.junit.Test; import org.keycloak.admin.client.resource.ClientTemplatesResource; import org.keycloak.admin.client.resource.RoleMappingResource; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.models.AccountRoles; import org.keycloak.models.Constants; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.representations.idm.ErrorRepresentation; import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.util.AdminEventPaths; import org.keycloak.testsuite.util.Matchers; import javax.ws.rs.BadRequestException; import javax.ws.rs.NotFoundException; import javax.ws.rs.core.Response; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.ws.rs.*; import javax.ws.rs.core.Response.Status; import static org.junit.Assert.*; /** * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> */ public class ClientTemplateTest extends AbstractClientTest { @Test public void testAddDuplicatedTemplate() { ClientTemplateRepresentation templateRep = new ClientTemplateRepresentation(); templateRep.setName("template1"); String templateId = createTemplate(templateRep); templateRep = new ClientTemplateRepresentation(); templateRep.setName("template1"); Response response = clientTemplates().create(templateRep); assertEquals(409, response.getStatus()); ErrorRepresentation error = response.readEntity(ErrorRepresentation.class); Assert.assertEquals("Client Template template1 already exists", error.getErrorMessage()); // Cleanup removeTemplate(templateId); } @Test (expected = NotFoundException.class) public void testGetUnknownTemplate() { clientTemplates().get("unknown-id").toRepresentation(); } @Test public void testRemoveTemplate() { // Create template1 ClientTemplateRepresentation templateRep = new ClientTemplateRepresentation(); templateRep.setName("template1"); String template1Id = createTemplate(templateRep); List<ClientTemplateRepresentation> clientTemplates = clientTemplates().findAll(); Assert.assertEquals(1, clientTemplates.size()); Assert.assertEquals("template1", clientTemplates.get(0).getName()); // Create template2 templateRep = new ClientTemplateRepresentation(); templateRep.setName("template2"); String template2Id = createTemplate(templateRep); clientTemplates = clientTemplates().findAll(); Assert.assertEquals(2, clientTemplates.size()); // Remove template1 removeTemplate(template1Id); clientTemplates = clientTemplates().findAll(); Assert.assertEquals(1, clientTemplates.size()); Assert.assertEquals("template2", clientTemplates.get(0).getName()); // Remove template2 removeTemplate(template2Id); clientTemplates = clientTemplates().findAll(); Assert.assertEquals(0, clientTemplates.size()); } @Test public void testUpdateTemplate() { // Test creating ClientTemplateRepresentation templateRep = new ClientTemplateRepresentation(); templateRep.setName("template1"); templateRep.setDescription("template1-desc"); templateRep.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); templateRep.setFullScopeAllowed(true); String template1Id = createTemplate(templateRep); // Assert created attributes templateRep = clientTemplates().get(template1Id).toRepresentation(); Assert.assertEquals("template1", templateRep.getName()); Assert.assertEquals("template1-desc", templateRep.getDescription()); Assert.assertEquals(OIDCLoginProtocol.LOGIN_PROTOCOL, templateRep.getProtocol()); Assert.assertEquals(true, templateRep.isFullScopeAllowed()); // Test updating templateRep.setName("template1-updated"); templateRep.setDescription("template1-desc-updated"); templateRep.setProtocol(SamlProtocol.LOGIN_PROTOCOL); templateRep.setFullScopeAllowed(false); clientTemplates().get(template1Id).update(templateRep); assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientTemplateResourcePath(template1Id), templateRep, ResourceType.CLIENT_TEMPLATE); // Assert updated attributes templateRep = clientTemplates().get(template1Id).toRepresentation(); Assert.assertEquals("template1-updated", templateRep.getName()); Assert.assertEquals("template1-desc-updated", templateRep.getDescription()); Assert.assertEquals(SamlProtocol.LOGIN_PROTOCOL, templateRep.getProtocol()); Assert.assertEquals(false, templateRep.isFullScopeAllowed()); // Remove template1 clientTemplates().get(template1Id).remove(); } @Test public void testRenameTemplate() { // Create two templates ClientTemplateRepresentation template1Rep = new ClientTemplateRepresentation(); template1Rep.setName("template1"); template1Rep.setDescription("template1-desc"); template1Rep.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); template1Rep.setFullScopeAllowed(true); createTemplate(template1Rep); ClientTemplateRepresentation template2Rep = new ClientTemplateRepresentation(); template2Rep.setName("template2"); template2Rep.setDescription("template2-desc"); template2Rep.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); template2Rep.setFullScopeAllowed(true); String template2Id = createTemplate(template2Rep); // Test updating template2Rep.setName("template1"); try { clientTemplates().get(template2Id).update(template2Rep); } catch (ClientErrorException ex) { assertThat(ex.getResponse(), Matchers.statusCodeIs(Status.CONFLICT)); } } @Test public void testScopes() { // Add realm role1 RoleRepresentation roleRep1 = createRealmRole("role1"); // Add realm role2 RoleRepresentation roleRep2 = createRealmRole("role2"); // Add role2 as composite to role1 testRealmResource().roles().get("role1").addComposites(Collections.singletonList(roleRep2)); assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.roleResourceCompositesPath("role1"), Collections.singletonList(roleRep2), ResourceType.REALM_ROLE); // create client template ClientTemplateRepresentation templateRep = new ClientTemplateRepresentation(); templateRep.setName("bar-template"); templateRep.setFullScopeAllowed(false); String templateId = createTemplate(templateRep); // update with some scopes String accountMgmtId = testRealmResource().clients().findByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).get(0).getId(); RoleRepresentation viewAccountRoleRep = testRealmResource().clients().get(accountMgmtId).roles().get(AccountRoles.VIEW_PROFILE).toRepresentation(); RoleMappingResource scopesResource = clientTemplates().get(templateId).getScopeMappings(); scopesResource.realmLevel().add(Collections.singletonList(roleRep1)); assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateScopeMappingsRealmLevelPath(templateId), Collections.singletonList(roleRep1), ResourceType.REALM_SCOPE_MAPPING); scopesResource.clientLevel(accountMgmtId).add(Collections.singletonList(viewAccountRoleRep)); assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateScopeMappingsClientLevelPath(templateId, accountMgmtId), Collections.singletonList(viewAccountRoleRep), ResourceType.CLIENT_SCOPE_MAPPING); // test that scopes are available (also through composite role) List<RoleRepresentation> allRealm = scopesResource.realmLevel().listAll(); List<RoleRepresentation> availableRealm = scopesResource.realmLevel().listAvailable(); List<RoleRepresentation> effectiveRealm = scopesResource.realmLevel().listEffective(); List<RoleRepresentation> accountRoles = scopesResource.clientLevel(accountMgmtId).listAll(); assertRolesPresent(allRealm, "role1"); assertRolesNotPresent(availableRealm, "role1", "role2"); assertRolesPresent(effectiveRealm, "role1", "role2"); assertRolesPresent(accountRoles, AccountRoles.VIEW_PROFILE); MappingsRepresentation mappingsRep = clientTemplates().get(templateId).getScopeMappings().getAll(); assertRolesPresent(mappingsRep.getRealmMappings(), "role1"); assertRolesPresent(mappingsRep.getClientMappings().get(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getMappings(), AccountRoles.VIEW_PROFILE); // remove scopes scopesResource.realmLevel().remove(Collections.singletonList(roleRep1)); assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientTemplateScopeMappingsRealmLevelPath(templateId), Collections.singletonList(roleRep1), ResourceType.REALM_SCOPE_MAPPING); scopesResource.clientLevel(accountMgmtId).remove(Collections.singletonList(viewAccountRoleRep)); assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientTemplateScopeMappingsClientLevelPath(templateId, accountMgmtId), Collections.singletonList(viewAccountRoleRep), ResourceType.CLIENT_SCOPE_MAPPING); // assert scopes are removed allRealm = scopesResource.realmLevel().listAll(); availableRealm = scopesResource.realmLevel().listAvailable(); effectiveRealm = scopesResource.realmLevel().listEffective(); accountRoles = scopesResource.clientLevel(accountMgmtId).listAll(); assertRolesNotPresent(allRealm, "role1"); assertRolesPresent(availableRealm, "role1", "role2"); assertRolesNotPresent(effectiveRealm, "role1", "role2"); assertRolesNotPresent(accountRoles, AccountRoles.VIEW_PROFILE); // remove template removeTemplate(templateId); } private void assertRolesPresent(List<RoleRepresentation> roles, String... expectedRoleNames) { List<String> expectedList = Arrays.asList(expectedRoleNames); Set<String> presentRoles = new HashSet<>(); for (RoleRepresentation roleRep : roles) { presentRoles.add(roleRep.getName()); } for (String expected : expectedList) { if (!presentRoles.contains(expected)) { Assert.fail("Expected role " + expected + " not available"); } } } private void assertRolesNotPresent(List<RoleRepresentation> roles, String... notExpectedRoleNames) { List<String> notExpectedList = Arrays.asList(notExpectedRoleNames); for (RoleRepresentation roleRep : roles) { if (notExpectedList.contains(roleRep.getName())) { Assert.fail("Role " + roleRep.getName() + " wasn't expected to be available"); } } } // KEYCLOAK-2809 @Test public void testRemoveScopedRole() { // Add realm role RoleRepresentation roleRep = createRealmRole("foo-role"); // Add client template ClientTemplateRepresentation templateRep = new ClientTemplateRepresentation(); templateRep.setName("bar-template"); templateRep.setFullScopeAllowed(false); String templateId = createTemplate(templateRep); // Add realm role to scopes of clientTemplate clientTemplates().get(templateId).getScopeMappings().realmLevel().add(Collections.singletonList(roleRep)); assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateScopeMappingsRealmLevelPath(templateId), Collections.singletonList(roleRep), ResourceType.REALM_SCOPE_MAPPING); List<RoleRepresentation> roleReps = clientTemplates().get(templateId).getScopeMappings().realmLevel().listAll(); Assert.assertEquals(1, roleReps.size()); Assert.assertEquals("foo-role", roleReps.get(0).getName()); // Remove realm role testRealmResource().roles().deleteRole("foo-role"); assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.roleResourcePath("foo-role"), ResourceType.REALM_ROLE); // Get scope mappings roleReps = clientTemplates().get(templateId).getScopeMappings().realmLevel().listAll(); Assert.assertEquals(0, roleReps.size()); // Cleanup removeTemplate(templateId); } private RoleRepresentation createRealmRole(String roleName) { RoleRepresentation roleRep = new RoleRepresentation(); roleRep.setName(roleName); testRealmResource().roles().create(roleRep); assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.roleResourcePath(roleName), roleRep, ResourceType.REALM_ROLE); return testRealmResource().roles().get(roleName).toRepresentation(); } // KEYCLOAK-2844 @Test public void testRemoveTemplateInUse() { // Add client template ClientTemplateRepresentation templateRep = new ClientTemplateRepresentation(); templateRep.setName("foo-template"); templateRep.setFullScopeAllowed(false); String templateId = createTemplate(templateRep); // Add client with the clientTemplate ClientRepresentation clientRep = new ClientRepresentation(); clientRep.setClientId("bar-client"); clientRep.setName("bar-client"); clientRep.setRootUrl("foo"); clientRep.setProtocol("openid-connect"); clientRep.setClientTemplate("foo-template"); String clientDbId = createClient(clientRep); // Can't remove clientTemplate try { clientTemplates().get(templateId).remove(); } catch (BadRequestException bre) { ErrorRepresentation error = bre.getResponse().readEntity(ErrorRepresentation.class); Assert.assertEquals("Cannot remove client template, it is currently in use", error.getErrorMessage()); assertAdminEvents.assertEmpty(); } // Remove client removeClient(clientDbId); // Can remove clientTemplate now removeTemplate(templateId); } private ClientTemplatesResource clientTemplates() { return testRealmResource().clientTemplates(); } private String createTemplate(ClientTemplateRepresentation templateRep) { Response resp = clientTemplates().create(templateRep); Assert.assertEquals(201, resp.getStatus()); resp.close(); String templateId = ApiUtil.getCreatedId(resp); assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateResourcePath(templateId), templateRep, ResourceType.CLIENT_TEMPLATE); return templateId; } private void removeTemplate(String templateId) { clientTemplates().get(templateId).remove(); assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientTemplateResourcePath(templateId), ResourceType.CLIENT_TEMPLATE); } }