/* * 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.federation.storage.ldap; import org.junit.Assert; import org.junit.ClassRule; import org.junit.FixMethodOrder; import org.junit.Rule; import org.junit.Test; import org.junit.runners.MethodSorters; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.component.ComponentModel; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.ldap.LDAPStorageProvider; import org.keycloak.storage.ldap.LDAPStorageProviderFactory; import org.keycloak.storage.ldap.mappers.membership.LDAPGroupMapperMode; import org.keycloak.storage.ldap.mappers.membership.group.GroupLDAPStorageMapperFactory; import org.keycloak.storage.ldap.mappers.membership.group.GroupMapperConfig; import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.services.managers.RealmManager; import org.keycloak.storage.user.SynchronizationResult; import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.LDAPRule; /** * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class LDAPGroupMapper2WaySyncTest { @ClassRule public static LDAPRule ldapRule = new LDAPRule(); private static ComponentModel ldapModel = null; private static String descriptionAttrName = null; @Rule public KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() { @Override public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { MultivaluedHashMap<String,String> ldapConfig = LDAPTestUtils.getLdapRuleConfig(ldapRule); ldapConfig.putSingle(LDAPConstants.SYNC_REGISTRATIONS, "true"); ldapConfig.putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.WRITABLE.toString()); ldapConfig.putSingle(LDAPConstants.BATCH_SIZE_FOR_SYNC, "4"); // Issues with pagination on ApacheDS UserStorageProviderModel model = new UserStorageProviderModel(); model.setLastSync(0); model.setChangedSyncPeriod(-1); model.setFullSyncPeriod(-1); model.setName("test-ldap"); model.setPriority(0); model.setProviderId(LDAPStorageProviderFactory.PROVIDER_NAME); model.setConfig(ldapConfig); ldapModel = appRealm.addComponentModel(model); LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel); descriptionAttrName = ldapFedProvider.getLdapIdentityStore().getConfig().isActiveDirectory() ? "displayName" : "description"; // Add group mapper LDAPTestUtils.addOrUpdateGroupMapper(appRealm, ldapModel, LDAPGroupMapperMode.LDAP_ONLY, descriptionAttrName); // Remove all LDAP groups LDAPTestUtils.removeAllLDAPGroups(session, appRealm, ldapModel, "groupsMapper"); // Add some groups for testing into Keycloak removeAllModelGroups(appRealm); GroupModel group1 = appRealm.createGroup("group1"); appRealm.moveGroup(group1, null); group1.setSingleAttribute(descriptionAttrName, "group1 - description1"); GroupModel group11 = appRealm.createGroup("group11"); appRealm.moveGroup(group11, group1); GroupModel group12 = appRealm.createGroup("group12"); appRealm.moveGroup(group12, group1); group12.setSingleAttribute(descriptionAttrName, "group12 - description12"); GroupModel group2 = appRealm.createGroup("group2"); appRealm.moveGroup(group2, null); } }); @Test public void test01_syncNoPreserveGroupInheritance() throws Exception { KeycloakSession session = keycloakRule.startSession(); try { RealmModel realm = session.realms().getRealmByName("test"); ComponentModel mapperModel = LDAPTestUtils.getSubcomponentByName(realm,ldapModel, "groupsMapper"); LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel); // Update group mapper to skip preserve inheritance and check it will pass now LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel, GroupMapperConfig.PRESERVE_GROUP_INHERITANCE, "false"); realm.updateComponent(mapperModel); // Sync from Keycloak into LDAP SynchronizationResult syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromKeycloakToFederationProvider(realm); LDAPTestUtils.assertSyncEquals(syncResult, 4, 0, 0, 0); } finally { keycloakRule.stopSession(session, true); } session = keycloakRule.startSession(); try { RealmModel realm = session.realms().getRealmByName("test"); // Delete all KC groups now removeAllModelGroups(realm); Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group1")); Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group11")); Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group2")); } finally { keycloakRule.stopSession(session, true); } session = keycloakRule.startSession(); try { RealmModel realm = session.realms().getRealmByName("test"); ComponentModel mapperModel = LDAPTestUtils.getSubcomponentByName(realm,ldapModel, "groupsMapper"); LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel); // Sync from LDAP back into Keycloak SynchronizationResult syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(realm); LDAPTestUtils.assertSyncEquals(syncResult, 4, 0, 0, 0); // Assert groups are imported to keycloak. All are at top level GroupModel kcGroup1 = KeycloakModelUtils.findGroupByPath(realm, "/group1"); GroupModel kcGroup11 = KeycloakModelUtils.findGroupByPath(realm, "/group11"); GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group12"); GroupModel kcGroup2 = KeycloakModelUtils.findGroupByPath(realm, "/group2"); Assert.assertEquals(0, kcGroup1.getSubGroups().size()); Assert.assertEquals("group1 - description1", kcGroup1.getFirstAttribute(descriptionAttrName)); Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName)); Assert.assertEquals("group12 - description12", kcGroup12.getFirstAttribute(descriptionAttrName)); Assert.assertNull(kcGroup2.getFirstAttribute(descriptionAttrName)); // test drop non-existing works testDropNonExisting(session, realm, mapperModel, ldapProvider); } finally { keycloakRule.stopSession(session, true); } } @Test public void test02_syncWithGroupInheritance() throws Exception { KeycloakSession session = keycloakRule.startSession(); try { RealmModel realm = session.realms().getRealmByName("test"); ComponentModel mapperModel = LDAPTestUtils.getSubcomponentByName(realm,ldapModel, "groupsMapper"); LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel); // Update group mapper to skip preserve inheritance and check it will pass now LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel, GroupMapperConfig.PRESERVE_GROUP_INHERITANCE, "true"); realm.updateComponent(mapperModel); // Sync from Keycloak into LDAP SynchronizationResult syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromKeycloakToFederationProvider(realm); LDAPTestUtils.assertSyncEquals(syncResult, 4, 0, 0, 0); } finally { keycloakRule.stopSession(session, true); } session = keycloakRule.startSession(); try { RealmModel realm = session.realms().getRealmByName("test"); // Delete all KC groups now removeAllModelGroups(realm); Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group1")); Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group11")); Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group2")); } finally { keycloakRule.stopSession(session, true); } session = keycloakRule.startSession(); try { RealmModel realm = session.realms().getRealmByName("test"); ComponentModel mapperModel = LDAPTestUtils.getSubcomponentByName(realm,ldapModel, "groupsMapper"); LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel); // Sync from LDAP back into Keycloak SynchronizationResult syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(realm); LDAPTestUtils.assertSyncEquals(syncResult, 4, 0, 0, 0); // Assert groups are imported to keycloak. All are at top level GroupModel kcGroup1 = KeycloakModelUtils.findGroupByPath(realm, "/group1"); GroupModel kcGroup11 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group11"); GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group12"); GroupModel kcGroup2 = KeycloakModelUtils.findGroupByPath(realm, "/group2"); Assert.assertEquals(2, kcGroup1.getSubGroups().size()); Assert.assertEquals("group1 - description1", kcGroup1.getFirstAttribute(descriptionAttrName)); Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName)); Assert.assertEquals("group12 - description12", kcGroup12.getFirstAttribute(descriptionAttrName)); Assert.assertNull(kcGroup2.getFirstAttribute(descriptionAttrName)); // test drop non-existing works testDropNonExisting(session, realm, mapperModel, ldapProvider); } finally { keycloakRule.stopSession(session, true); } } private static void removeAllModelGroups(RealmModel appRealm) { for (GroupModel group : appRealm.getTopLevelGroups()) { appRealm.removeGroup(group); } } private void testDropNonExisting(KeycloakSession session, RealmModel realm, ComponentModel mapperModel, LDAPStorageProvider ldapProvider) { // Put some group directly to LDAP LDAPTestUtils.createLDAPGroup(session, realm, ldapModel, "group3"); // Sync and assert our group is still in LDAP SynchronizationResult syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromKeycloakToFederationProvider(realm); LDAPTestUtils.assertSyncEquals(syncResult, 0, 4, 0, 0); Assert.assertNotNull(LDAPTestUtils.getGroupMapper(mapperModel, ldapProvider, realm).loadLDAPGroupByName("group3")); // Change config to drop non-existing groups LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel, GroupMapperConfig.DROP_NON_EXISTING_GROUPS_DURING_SYNC, "true"); realm.updateComponent(mapperModel); // Sync and assert group removed from LDAP syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromKeycloakToFederationProvider(realm); LDAPTestUtils.assertSyncEquals(syncResult, 0, 4, 1, 0); Assert.assertNull(LDAPTestUtils.getGroupMapper(mapperModel, ldapProvider, realm).loadLDAPGroupByName("group3")); } }