/* * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @author tags. All rights reserved. * * 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.partialimport; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.IdentityProviderResource; import org.keycloak.admin.client.resource.RoleResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.events.admin.OperationType; import org.keycloak.partialimport.PartialImportResult; import org.keycloak.partialimport.PartialImportResults; import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.PartialImportRepresentation; import org.keycloak.representations.idm.PartialImportRepresentation.Policy; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.RolesRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.AbstractAuthTest; import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.util.AssertAdminEvents; import org.keycloak.testsuite.util.RealmBuilder; import javax.ws.rs.core.Response; import java.util.ArrayList; 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 static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; /** * Tests for the partial import endpoint in admin client. Also tests the * server side functionality of each resource along with "fail, skip, overwrite" * functions. * * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc. */ public class PartialImportTest extends AbstractAuthTest { @Rule public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this); private static final int NUM_RESOURCE_TYPES = 6; private static final String CLIENT_ROLES_CLIENT = "clientRolesClient"; private static final String USER_PREFIX = "user"; private static final String GROUP_PREFIX = "group"; private static final String CLIENT_PREFIX = "client"; private static final String REALM_ROLE_PREFIX = "realmRole"; private static final String CLIENT_ROLE_PREFIX = "clientRole"; private static final String[] IDP_ALIASES = {"twitter", "github", "facebook", "google", "linkedin", "microsoft", "stackoverflow"}; private static final int NUM_ENTITIES = IDP_ALIASES.length; private PartialImportRepresentation piRep; private String realmId; @Before public void initAdminEvents() { RealmRepresentation realmRep = RealmBuilder.edit(testRealmResource().toRepresentation()).testEventListener().build(); realmId = realmRep.getId(); realmRep.setDuplicateEmailsAllowed(false); adminClient.realm(realmRep.getRealm()).update(realmRep); piRep = new PartialImportRepresentation(); } @After public void tearDownAdminEvents() { RealmRepresentation realmRep = RealmBuilder.edit(testRealmResource().toRepresentation()).removeTestEventListener().build(); adminClient.realm(realmRep.getRealm()).update(realmRep); } @Before public void createClientForClientRoles() { ClientRepresentation client = new ClientRepresentation(); client.setClientId(CLIENT_ROLES_CLIENT); client.setName(CLIENT_ROLES_CLIENT); client.setRootUrl("foo"); client.setProtocol("openid-connect"); Response resp = testRealmResource().clients().create(client); // for some reason, findAll() will later fail unless readEntity is called here resp.readEntity(String.class); //testRealmResource().clients().findAll(); } @Before public void removeUsers() { List<UserRepresentation> toRemove = testRealmResource().users().search(USER_PREFIX, 0, NUM_ENTITIES); for (UserRepresentation user : toRemove) { testRealmResource().users().get(user.getId()).remove(); } } @Before public void removeGroups() { List<GroupRepresentation> toRemove = testRealmResource().groups().groups(); for (GroupRepresentation group: toRemove) { testRealmResource().groups().group(group.getId()).remove(); } } @Before public void removeClients() { List<ClientRepresentation> toRemove = testRealmResource().clients().findAll(); for (ClientRepresentation client : toRemove) { if (client.getName() != null && client.getName().startsWith(CLIENT_PREFIX)) { testRealmResource().clients().get(client.getId()).remove(); } } } @Before public void removeProviders() { List<IdentityProviderRepresentation> toRemove = testRealmResource().identityProviders().findAll(); for (IdentityProviderRepresentation idp : toRemove) { testRealmResource().identityProviders().get(idp.getInternalId()).remove(); } } @Before public void removeRealmRoles() { List<RoleRepresentation> toRemove = testRealmResource().roles().list(); for (RoleRepresentation role : toRemove) { if (role.getName().startsWith(REALM_ROLE_PREFIX)) { testRealmResource().roles().get(role.getName()).remove(); } } } @Before public void removeClientRoles() { List<RoleRepresentation> toRemove = clientRolesClient().roles().list(); for (RoleRepresentation role : toRemove) { if (role.getName().startsWith(CLIENT_ROLE_PREFIX)) { testRealmResource().clients().get(CLIENT_ROLES_CLIENT).roles().get(role.getName()).remove(); } } } private ClientResource clientRolesClient() { return ApiUtil.findClientResourceByName(testRealmResource(), CLIENT_ROLES_CLIENT); } private void setFail() { piRep.setIfResourceExists(Policy.FAIL.toString()); } private void setSkip() { piRep.setIfResourceExists(Policy.SKIP.toString()); } private void setOverwrite() { piRep.setIfResourceExists(Policy.OVERWRITE.toString()); } private PartialImportResults doImport() { Response response = testRealmResource().partialImport(piRep); return response.readEntity(PartialImportResults.class); } private void addUsers() { List<UserRepresentation> users = new ArrayList<>(); for (int i = 0; i < NUM_ENTITIES; i++) { UserRepresentation user = createUserRepresentation(USER_PREFIX + i, USER_PREFIX + i + "@foo.com", "foo", "bar", true); users.add(user); } piRep.setUsers(users); } private void addUsersWithTermsAndConditions() { List<UserRepresentation> users = new ArrayList<>(); List<String> requiredActions = new ArrayList<>(); requiredActions.add("terms_and_conditions"); for (int i = 0; i < NUM_ENTITIES; i++) { UserRepresentation user = createUserRepresentation(USER_PREFIX + i, USER_PREFIX + i + "@foo.com", "foo", "bar", true); user.setRequiredActions(requiredActions); users.add(user); } piRep.setUsers(users); } private void addGroups() { List<GroupRepresentation> groups = new ArrayList<>(); for (int i=0; i < NUM_ENTITIES; i++) { GroupRepresentation group = new GroupRepresentation(); group.setName(GROUP_PREFIX + i); group.setPath("/" + GROUP_PREFIX + i); groups.add(group); } piRep.setGroups(groups); } private void addClients() { List<ClientRepresentation> clients = new ArrayList<>(); for (int i = 0; i < NUM_ENTITIES; i++) { ClientRepresentation client = new ClientRepresentation(); client.setClientId(CLIENT_PREFIX + i); client.setName(CLIENT_PREFIX + i); client.setRootUrl("foo"); clients.add(client); } piRep.setClients(clients); } private void addProviders() { List<IdentityProviderRepresentation> providers = new ArrayList<>(); for (String alias : IDP_ALIASES) { IdentityProviderRepresentation idpRep = new IdentityProviderRepresentation(); idpRep.setAlias(alias); idpRep.setProviderId(alias); idpRep.setEnabled(true); idpRep.setAuthenticateByDefault(false); idpRep.setFirstBrokerLoginFlowAlias("first broker login"); Map<String, String> config = new HashMap<>(); config.put("clientSecret", "secret"); config.put("clientId", alias); idpRep.setConfig(config); providers.add(idpRep); } piRep.setIdentityProviders(providers); } private List<RoleRepresentation> makeRoles(String prefix) { List<RoleRepresentation> roles = new ArrayList<>(); for (int i = 0; i < NUM_ENTITIES; i++) { RoleRepresentation role = new RoleRepresentation(); role.setName(prefix + i); roles.add(role); } return roles; } private void addRealmRoles() { RolesRepresentation roles = piRep.getRoles(); if (roles == null) roles = new RolesRepresentation(); roles.setRealm(makeRoles(REALM_ROLE_PREFIX)); piRep.setRoles(roles); } private void addClientRoles() { RolesRepresentation roles = piRep.getRoles(); if (roles == null) roles = new RolesRepresentation(); Map<String, List<RoleRepresentation>> clientRolesMap = new HashMap<>(); clientRolesMap.put(CLIENT_ROLES_CLIENT, makeRoles(CLIENT_ROLE_PREFIX)); roles.setClient(clientRolesMap); piRep.setRoles(roles); } @Test public void testAddUsers() { assertAdminEvents.clear(); setFail(); addUsers(); PartialImportResults results = doImport(); assertEquals(NUM_ENTITIES, results.getAdded()); // Need to do this way as admin events from partial import are unsorted Set<String> userIds = new HashSet<>(); for (int i=0 ; i<NUM_ENTITIES ; i++) { AdminEventRepresentation adminEvent = assertAdminEvents.poll(); Assert.assertEquals(realmId, adminEvent.getRealmId()); Assert.assertEquals(OperationType.CREATE.name(), adminEvent.getOperationType()); Assert.assertTrue(adminEvent.getResourcePath().startsWith("users/")); String userId = adminEvent.getResourcePath().substring(6); userIds.add(userId); } assertAdminEvents.assertEmpty(); for (PartialImportResult result : results.getResults()) { String id = result.getId(); UserResource userRsc = testRealmResource().users().get(id); UserRepresentation user = userRsc.toRepresentation(); assertTrue(user.getUsername().startsWith(USER_PREFIX)); Assert.assertTrue(userIds.contains(id)); } } @Test public void testAddUsersWithDuplicateEmailsForbidden() { assertAdminEvents.clear(); setFail(); addUsers(); UserRepresentation user = createUserRepresentation(USER_PREFIX + 999, USER_PREFIX + 1 + "@foo.com", "foo", "bar", true); piRep.getUsers().add(user); Response response = testRealmResource().partialImport(piRep); assertEquals(409, response.getStatus()); } @Test public void testAddUsersWithDuplicateEmailsAllowed() { RealmRepresentation realmRep = testRealmResource().toRepresentation(); realmRep.setDuplicateEmailsAllowed(true); testRealmResource().update(realmRep); assertAdminEvents.clear(); setFail(); addUsers(); doImport(); UserRepresentation user = createUserRepresentation(USER_PREFIX + 999, USER_PREFIX + 1 + "@foo.com", "foo", "bar", true); piRep.setUsers(Arrays.asList(user)); PartialImportResults results = doImport(); assertEquals(1, results.getAdded()); } @Test public void testAddUsersWithTermsAndConditions() { assertAdminEvents.clear(); setFail(); addUsersWithTermsAndConditions(); PartialImportResults results = doImport(); assertEquals(NUM_ENTITIES, results.getAdded()); // Need to do this way as admin events from partial import are unsorted Set<String> userIds = new HashSet<>(); for (int i=0 ; i<NUM_ENTITIES ; i++) { AdminEventRepresentation adminEvent = assertAdminEvents.poll(); Assert.assertEquals(realmId, adminEvent.getRealmId()); Assert.assertEquals(OperationType.CREATE.name(), adminEvent.getOperationType()); Assert.assertTrue(adminEvent.getResourcePath().startsWith("users/")); String userId = adminEvent.getResourcePath().substring(6); userIds.add(userId); } assertAdminEvents.assertEmpty(); for (PartialImportResult result : results.getResults()) { String id = result.getId(); UserResource userRsc = testRealmResource().users().get(id); UserRepresentation user = userRsc.toRepresentation(); assertTrue(user.getUsername().startsWith(USER_PREFIX)); Assert.assertTrue(userIds.contains(id)); } } @Test public void testAddClients() { setFail(); addClients(); PartialImportResults results = doImport(); assertEquals(NUM_ENTITIES, results.getAdded()); for (PartialImportResult result : results.getResults()) { String id = result.getId(); ClientResource clientRsc = testRealmResource().clients().get(id); ClientRepresentation client = clientRsc.toRepresentation(); assertTrue(client.getName().startsWith(CLIENT_PREFIX)); } } @Test public void testAddProviders() { setFail(); addProviders(); PartialImportResults results = doImport(); assertEquals(IDP_ALIASES.length, results.getAdded()); for (PartialImportResult result : results.getResults()) { String id = result.getId(); IdentityProviderResource idpRsc = testRealmResource().identityProviders().get(id); IdentityProviderRepresentation idp = idpRsc.toRepresentation(); Map<String, String> config = idp.getConfig(); assertTrue(Arrays.asList(IDP_ALIASES).contains(config.get("clientId"))); } } @Test public void testAddRealmRoles() { setFail(); addRealmRoles(); PartialImportResults results = doImport(); assertEquals(NUM_ENTITIES, results.getAdded()); for (PartialImportResult result : results.getResults()) { String name = result.getResourceName(); RoleResource roleRsc = testRealmResource().roles().get(name); RoleRepresentation role = roleRsc.toRepresentation(); assertTrue(role.getName().startsWith(REALM_ROLE_PREFIX)); } } @Test public void testAddClientRoles() { setFail(); addClientRoles(); PartialImportResults results = doImport(); assertEquals(NUM_ENTITIES, results.getAdded()); List<RoleRepresentation> clientRoles = clientRolesClient().roles().list(); assertEquals(NUM_ENTITIES, clientRoles.size()); for (RoleRepresentation roleRep : clientRoles) { assertTrue(roleRep.getName().startsWith(CLIENT_ROLE_PREFIX)); } } private void testFail() { setFail(); PartialImportResults results = doImport(); assertNull(results.getErrorMessage()); results = doImport(); // second time should fail assertNotNull(results.getErrorMessage()); } @Test public void testAddUsersFail() { addUsers(); testFail(); } @Test public void testAddGroupsFail() { addGroups(); testFail(); } @Test public void testAddClientsFail() { addClients(); testFail(); } @Test public void testAddProvidersFail() { addProviders(); testFail(); } @Test public void testAddRealmRolesFail() { addRealmRoles(); testFail(); } @Test public void testAddClientRolesFail() { addClientRoles(); testFail(); } private void testSkip() { setSkip(); PartialImportResults results = doImport(); assertEquals(NUM_ENTITIES, results.getAdded()); results = doImport(); assertEquals(NUM_ENTITIES, results.getSkipped()); } @Test public void testAddUsersSkip() { addUsers(); testSkip(); } @Test public void testAddGroupsSkip() { addGroups(); testSkip(); } @Test public void testAddClientsSkip() { addClients(); testSkip(); } @Test public void testAddProvidersSkip() { addProviders(); testSkip(); } @Test public void testAddRealmRolesSkip() { addRealmRoles(); testSkip(); } @Test public void testAddClientRolesSkip() { addClientRoles(); testSkip(); } private void testOverwrite() { setOverwrite(); PartialImportResults results = doImport(); assertEquals(NUM_ENTITIES, results.getAdded()); results = doImport(); assertEquals(NUM_ENTITIES, results.getOverwritten()); } @Test public void testAddUsersOverwrite() { addUsers(); testOverwrite(); } @Test public void testAddGroupsOverwrite() { addGroups(); testOverwrite(); } @Test public void testAddClientsOverwrite() { addClients(); testOverwrite(); } @Test public void testAddProvidersOverwrite() { addProviders(); testOverwrite(); } @Test public void testAddRealmRolesOverwrite() { addRealmRoles(); testOverwrite(); } @Test public void testAddClientRolesOverwrite() { addClientRoles(); testOverwrite(); } private void importEverything() { addUsers(); addGroups(); addClients(); addProviders(); addRealmRoles(); addClientRoles(); PartialImportResults results = doImport(); assertNull(results.getErrorMessage()); assertEquals(NUM_ENTITIES * NUM_RESOURCE_TYPES, results.getAdded()); } @Test public void testEverythingFail() { setFail(); importEverything(); PartialImportResults results = doImport(); // second import will fail because not allowed to skip or overwrite assertNotNull(results.getErrorMessage()); } @Test public void testEverythingSkip() { setSkip(); importEverything(); PartialImportResults results = doImport(); assertEquals(NUM_ENTITIES * NUM_RESOURCE_TYPES, results.getSkipped()); } @Test public void testEverythingOverwrite() { setOverwrite(); importEverything(); PartialImportResults results = doImport(); assertEquals(NUM_ENTITIES * NUM_RESOURCE_TYPES, results.getOverwritten()); } //KEYCLOAK-3042 @Test public void testOverwriteExistingClientWithRoles() { setOverwrite(); ClientRepresentation client = adminClient.realm(MASTER).clients().findByClientId("broker").get(0); List<RoleRepresentation> clientRoles = adminClient.realm(MASTER).clients().get(client.getId()).roles().list(); Map<String, List<RoleRepresentation>> clients = new HashMap<>(); clients.put(client.getClientId(), clientRoles); RolesRepresentation roles = new RolesRepresentation(); roles.setClient(clients); piRep.setClients(Arrays.asList(client)); piRep.setRoles(roles); doImport(); } }