ldap port
This commit is contained in:
parent
c75dcb90c2
commit
4302b440ee
21 changed files with 2063 additions and 35 deletions
|
@ -157,6 +157,7 @@ public class UserAttributeLDAPStorageMapper extends AbstractLDAPStorageMapper {
|
|||
|
||||
// throw ModelDuplicateException if there is different user in model with same email
|
||||
protected void checkDuplicateEmail(String userModelAttrName, String email, RealmModel realm, KeycloakSession session, UserModel user) {
|
||||
if (email == null) return;
|
||||
if (UserModel.EMAIL.equalsIgnoreCase(userModelAttrName)) {
|
||||
// lowercase before search
|
||||
email = KeycloakModelUtils.toLowerCaseSafe(email);
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.keycloak.models.ClientTemplateModel;
|
|||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OTPPolicy;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -39,6 +40,7 @@ import org.keycloak.models.UserFederationProviderModel;
|
|||
import org.keycloak.models.cache.CachedRealmModel;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.PrivateKey;
|
||||
|
@ -62,10 +64,12 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
protected RealmCacheSession cacheSession;
|
||||
protected RealmModel updated;
|
||||
protected RealmCache cache;
|
||||
protected KeycloakSession session;
|
||||
|
||||
public RealmAdapter(CachedRealm cached, RealmCacheSession cacheSession) {
|
||||
public RealmAdapter(KeycloakSession session, CachedRealm cached, RealmCacheSession cacheSession) {
|
||||
this.cached = cached;
|
||||
this.cacheSession = cacheSession;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1333,12 +1337,28 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
@Override
|
||||
public ComponentModel addComponentModel(ComponentModel model) {
|
||||
getDelegateForUpdate();
|
||||
evictUsers(model);
|
||||
return updated.addComponentModel(model);
|
||||
}
|
||||
|
||||
public void evictUsers(ComponentModel model) {
|
||||
String parentId = model.getParentId();
|
||||
evictUsers(parentId);
|
||||
}
|
||||
|
||||
public void evictUsers(String parentId) {
|
||||
if (parentId != null && !parentId.equals(getId())) {
|
||||
ComponentModel parent = getComponent(parentId);
|
||||
if (parent != null && UserStorageProvider.class.getName().equals(parent.getProviderType())) {
|
||||
session.getUserCache().evict(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateComponent(ComponentModel component) {
|
||||
getDelegateForUpdate();
|
||||
evictUsers(component);
|
||||
updated.updateComponent(component);
|
||||
|
||||
}
|
||||
|
@ -1346,6 +1366,7 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
@Override
|
||||
public void removeComponent(ComponentModel component) {
|
||||
getDelegateForUpdate();
|
||||
evictUsers(component);
|
||||
updated.removeComponent(component);
|
||||
|
||||
}
|
||||
|
@ -1353,6 +1374,7 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
@Override
|
||||
public void removeComponents(String parentId) {
|
||||
getDelegateForUpdate();
|
||||
evictUsers(parentId);
|
||||
updated.removeComponents(parentId);
|
||||
|
||||
}
|
||||
|
|
|
@ -389,7 +389,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
} else if (managedRealms.containsKey(id)) {
|
||||
return managedRealms.get(id);
|
||||
}
|
||||
RealmAdapter adapter = new RealmAdapter(cached, this);
|
||||
RealmAdapter adapter = new RealmAdapter(session, cached, this);
|
||||
if (wasCached) {
|
||||
CachedRealmModel.RealmCachedEvent event = new CachedRealmModel.RealmCachedEvent() {
|
||||
@Override
|
||||
|
|
|
@ -63,6 +63,11 @@ public class UserAdapter implements CachedUserModel {
|
|||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMarkedForEviction() {
|
||||
return updated != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
getDelegateForUpdate();
|
||||
|
|
|
@ -115,7 +115,10 @@ public class UserCacheSession implements UserCache {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void evict(RealmModel realm) {
|
||||
realmInvalidations.add(realm.getId());
|
||||
}
|
||||
|
||||
protected void runInvalidations() {
|
||||
for (String realmId : realmInvalidations) {
|
||||
|
|
|
@ -2046,7 +2046,10 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
|||
}
|
||||
c.setName(model.getName());
|
||||
c.setParentId(model.getParentId());
|
||||
if (model.getParentId() == null) c.setParentId(this.getId());
|
||||
if (model.getParentId() == null) {
|
||||
c.setParentId(this.getId());
|
||||
model.setParentId(this.getId());
|
||||
}
|
||||
c.setProviderType(model.getProviderType());
|
||||
c.setProviderId(model.getProviderId());
|
||||
c.setSubType(model.getSubType());
|
||||
|
|
|
@ -1964,7 +1964,10 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
}
|
||||
updateComponentEntity(entity, model);
|
||||
model.setId(entity.getId());
|
||||
if (model.getParentId() == null) entity.setParentId(this.getId());
|
||||
if (model.getParentId() == null) {
|
||||
entity.setParentId(this.getId());
|
||||
model.setParentId(this.getId());
|
||||
}
|
||||
realm.getComponentEntities().add(entity);
|
||||
updateRealm();
|
||||
ComponentUtil.notifyCreated(session, this, model);
|
||||
|
|
|
@ -35,6 +35,8 @@ public interface CachedUserModel extends UserModel {
|
|||
*/
|
||||
UserModel getDelegateForUpdate();
|
||||
|
||||
boolean isMarkedForEviction();
|
||||
|
||||
/**
|
||||
* Invalidate the cache for this model
|
||||
*
|
||||
|
|
|
@ -32,5 +32,12 @@ public interface UserCache extends UserProvider {
|
|||
* @param user
|
||||
*/
|
||||
void evict(RealmModel realm, UserModel user);
|
||||
|
||||
/**
|
||||
* Evict users of a specific realm
|
||||
*
|
||||
* @param realm
|
||||
*/
|
||||
void evict(RealmModel realm);
|
||||
void clear();
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.representations.idm.CertificateRepresentation;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.transaction.JtaTransactionManagerLookup;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
@ -372,6 +373,29 @@ public final class KeycloakModelUtils {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static UserStorageProviderModel findUserStorageProviderByName(String displayName, RealmModel realm) {
|
||||
if (displayName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (UserStorageProviderModel fedProvider : realm.getUserStorageProviders()) {
|
||||
if (displayName.equals(fedProvider.getName())) {
|
||||
return fedProvider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static UserStorageProviderModel findUserStorageProviderById(String fedProviderId, RealmModel realm) {
|
||||
for (UserStorageProviderModel fedProvider : realm.getUserStorageProviders()) {
|
||||
if (fedProviderId.equals(fedProvider.getId())) {
|
||||
return fedProvider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ComponentModel createComponentModel(String name, String parentId, String providerId, String providerType, String... config) {
|
||||
ComponentModel mapperModel = new ComponentModel();
|
||||
mapperModel.setParentId(parentId);
|
||||
|
|
|
@ -43,8 +43,9 @@ public class OTPCredentialProvider implements CredentialProvider, CredentialInpu
|
|||
protected KeycloakSession session;
|
||||
|
||||
protected List<CredentialModel> getCachedCredentials(UserModel user, String type) {
|
||||
if (!(user instanceof CachedUserModel)) return Collections.EMPTY_LIST;
|
||||
if (!(user instanceof CachedUserModel)) return null;
|
||||
CachedUserModel cached = (CachedUserModel)user;
|
||||
if (cached.isMarkedForEviction()) return null;
|
||||
List<CredentialModel> rtn = (List<CredentialModel>)cached.getCachedWith().get(OTPCredentialProvider.class.getName() + "." + type);
|
||||
if (rtn == null) return Collections.EMPTY_LIST;
|
||||
return rtn;
|
||||
|
@ -186,8 +187,9 @@ public class OTPCredentialProvider implements CredentialProvider, CredentialInpu
|
|||
}
|
||||
|
||||
protected boolean configuredForTOTP(RealmModel realm, UserModel user) {
|
||||
return !getCachedCredentials(user, CredentialModel.TOTP).isEmpty()
|
||||
|| !getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.TOTP).isEmpty();
|
||||
List<CredentialModel> cachedCredentials = getCachedCredentials(user, CredentialModel.TOTP);
|
||||
if (cachedCredentials == null) return !getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.TOTP).isEmpty();
|
||||
return !cachedCredentials.isEmpty();
|
||||
}
|
||||
|
||||
public static boolean validOTP(RealmModel realm, String token, String secret) {
|
||||
|
|
|
@ -59,7 +59,7 @@ public class PasswordCredentialProvider implements CredentialProvider, Credentia
|
|||
|
||||
public CredentialModel getPassword(RealmModel realm, UserModel user) {
|
||||
List<CredentialModel> passwords = null;
|
||||
if (user instanceof CachedUserModel) {
|
||||
if (user instanceof CachedUserModel && !((CachedUserModel)user).isMarkedForEviction()) {
|
||||
CachedUserModel cached = (CachedUserModel)user;
|
||||
passwords = (List<CredentialModel>)cached.getCachedWith().get(PASSWORD_CACHE_KEY);
|
||||
|
||||
|
|
|
@ -138,6 +138,12 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
|||
if (getFederatedStorage() != null) getFederatedStorage().preRemove(realm, user);
|
||||
StorageId storageId = new StorageId(user.getId());
|
||||
if (storageId.getProviderId() == null) {
|
||||
if (user.getFederationLink() != null) {
|
||||
UserStorageProvider provider = getStorageProvider(session, realm, user.getFederationLink());
|
||||
if (provider != null && provider instanceof UserRegistrationProvider) {
|
||||
((UserRegistrationProvider)provider).removeUser(realm, user);
|
||||
}
|
||||
}
|
||||
return localStorage().removeUser(realm, user);
|
||||
}
|
||||
UserRegistrationProvider registry = (UserRegistrationProvider)getStorageProvider(session, realm, storageId.getProviderId());
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* 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.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;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @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, LDAPStorageProviderFactory.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.getComponentByName(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(mapperModel, ldapProvider, session, 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.getComponentByName(realm,ldapModel, "groupsMapper");
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
|
||||
// Sync from LDAP back into Keycloak
|
||||
SynchronizationResult syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, session, 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.getComponentByName(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(mapperModel, ldapProvider, session, 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.getComponentByName(realm,ldapModel, "groupsMapper");
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
|
||||
// Sync from LDAP back into Keycloak
|
||||
SynchronizationResult syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, session, 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(mapperModel, ldapProvider, session, 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(mapperModel, ldapProvider, session, realm);
|
||||
LDAPTestUtils.assertSyncEquals(syncResult, 0, 4, 1, 0);
|
||||
Assert.assertNull(LDAPTestUtils.getGroupMapper(mapperModel, ldapProvider, realm).loadLDAPGroupByName("group3"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* 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.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
||||
import org.keycloak.storage.ldap.LDAPUtils;
|
||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||
import org.keycloak.storage.ldap.mappers.membership.LDAPGroupMapperMode;
|
||||
import org.keycloak.storage.ldap.mappers.membership.MembershipType;
|
||||
import org.keycloak.storage.ldap.mappers.membership.group.GroupLDAPStorageMapper;
|
||||
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.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class LDAPGroupMapperSyncTest {
|
||||
|
||||
private static LDAPRule ldapRule = new LDAPRule();
|
||||
|
||||
private static ComponentModel ldapModel = null;
|
||||
private static String descriptionAttrName = null;
|
||||
|
||||
private static 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, LDAPStorageProviderFactory.EditMode.WRITABLE.toString());
|
||||
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
|
||||
LDAPObject group1 = LDAPTestUtils.createLDAPGroup(manager.getSession(), appRealm, ldapModel, "group1", descriptionAttrName, "group1 - description");
|
||||
LDAPObject group11 = LDAPTestUtils.createLDAPGroup(manager.getSession(), appRealm, ldapModel, "group11");
|
||||
LDAPObject group12 = LDAPTestUtils.createLDAPGroup(manager.getSession(), appRealm, ldapModel, "group12", descriptionAttrName, "group12 - description");
|
||||
|
||||
LDAPUtils.addMember(ldapFedProvider, MembershipType.DN, LDAPConstants.MEMBER, group1, group11, false);
|
||||
LDAPUtils.addMember(ldapFedProvider, MembershipType.DN, LDAPConstants.MEMBER, group1, group12, true);
|
||||
}
|
||||
});
|
||||
|
||||
@ClassRule
|
||||
public static TestRule chain = RuleChain
|
||||
.outerRule(ldapRule)
|
||||
.around(keycloakRule);
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
List<GroupModel> kcGroups = realm.getTopLevelGroups();
|
||||
for (GroupModel kcGroup : kcGroups) {
|
||||
realm.removeGroup(kcGroup);
|
||||
}
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test01_syncNoPreserveGroupInheritance() throws Exception {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ComponentModel mapperModel = LDAPTestUtils.getComponentByName(realm,ldapModel, "groupsMapper");
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
GroupLDAPStorageMapper groupMapper = LDAPTestUtils.getGroupMapper(mapperModel, ldapProvider, realm);
|
||||
|
||||
// Add recursive group mapping to LDAP. Check that sync with preserve group inheritance will fail
|
||||
LDAPObject group1 = groupMapper.loadLDAPGroupByName("group1");
|
||||
LDAPObject group12 = groupMapper.loadLDAPGroupByName("group12");
|
||||
LDAPUtils.addMember(ldapProvider, MembershipType.DN, LDAPConstants.MEMBER, group12, group1, true);
|
||||
|
||||
try {
|
||||
new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, session, realm);
|
||||
Assert.fail("Not expected group sync to pass");
|
||||
} catch (ModelException expected) {
|
||||
Assert.assertTrue(expected.getMessage().contains("Recursion detected"));
|
||||
}
|
||||
|
||||
// Update group mapper to skip preserve inheritance and check it will pass now
|
||||
LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel, GroupMapperConfig.PRESERVE_GROUP_INHERITANCE, "false");
|
||||
realm.updateComponent(mapperModel);
|
||||
|
||||
new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, session, realm);
|
||||
|
||||
// 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");
|
||||
|
||||
Assert.assertEquals(0, kcGroup1.getSubGroups().size());
|
||||
|
||||
Assert.assertEquals("group1 - description", kcGroup1.getFirstAttribute(descriptionAttrName));
|
||||
Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName));
|
||||
Assert.assertEquals("group12 - description", kcGroup12.getFirstAttribute(descriptionAttrName));
|
||||
|
||||
// Cleanup - remove recursive mapping in LDAP
|
||||
LDAPUtils.deleteMember(ldapProvider, MembershipType.DN, LDAPConstants.MEMBER, group12, group1, true);
|
||||
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test02_syncWithGroupInheritance() throws Exception {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ComponentModel mapperModel = LDAPTestUtils.getComponentByName(realm,ldapModel, "groupsMapper");
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
GroupLDAPStorageMapper groupMapper = LDAPTestUtils.getGroupMapper(mapperModel, ldapProvider, realm);
|
||||
|
||||
// Sync groups with inheritance
|
||||
SynchronizationResult syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, session, realm);
|
||||
LDAPTestUtils.assertSyncEquals(syncResult, 3, 0, 0, 0);
|
||||
|
||||
// Assert groups are imported to keycloak including their inheritance from LDAP
|
||||
GroupModel kcGroup1 = KeycloakModelUtils.findGroupByPath(realm, "/group1");
|
||||
Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group11"));
|
||||
Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group12"));
|
||||
GroupModel kcGroup11 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group11");
|
||||
GroupModel kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group12");
|
||||
|
||||
Assert.assertEquals(2, kcGroup1.getSubGroups().size());
|
||||
|
||||
Assert.assertEquals("group1 - description", kcGroup1.getFirstAttribute(descriptionAttrName));
|
||||
Assert.assertNull(kcGroup11.getFirstAttribute(descriptionAttrName));
|
||||
Assert.assertEquals("group12 - description", kcGroup12.getFirstAttribute(descriptionAttrName));
|
||||
|
||||
// Update description attributes in LDAP
|
||||
LDAPObject group1 = groupMapper.loadLDAPGroupByName("group1");
|
||||
group1.setSingleAttribute(descriptionAttrName, "group1 - changed description");
|
||||
ldapProvider.getLdapIdentityStore().update(group1);
|
||||
|
||||
LDAPObject group12 = groupMapper.loadLDAPGroupByName("group12");
|
||||
group12.setAttribute(descriptionAttrName, null);
|
||||
ldapProvider.getLdapIdentityStore().update(group12);
|
||||
|
||||
// Sync and assert groups updated
|
||||
syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, session, realm);
|
||||
LDAPTestUtils.assertSyncEquals(syncResult, 0, 3, 0, 0);
|
||||
|
||||
// Assert attributes changed in keycloak
|
||||
kcGroup1 = KeycloakModelUtils.findGroupByPath(realm, "/group1");
|
||||
kcGroup12 = KeycloakModelUtils.findGroupByPath(realm, "/group1/group12");
|
||||
Assert.assertEquals("group1 - changed description", kcGroup1.getFirstAttribute(descriptionAttrName));
|
||||
Assert.assertNull(kcGroup12.getFirstAttribute(descriptionAttrName));
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test03_syncWithDropNonExistingGroups() throws Exception {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ComponentModel mapperModel = LDAPTestUtils.getComponentByName(realm,ldapModel, "groupsMapper");
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
|
||||
// Sync groups with inheritance
|
||||
SynchronizationResult syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, session, realm);
|
||||
LDAPTestUtils.assertSyncEquals(syncResult, 3, 0, 0, 0);
|
||||
|
||||
// Assert groups are imported to keycloak including their inheritance from LDAP
|
||||
GroupModel kcGroup1 = KeycloakModelUtils.findGroupByPath(realm, "/group1");
|
||||
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/group11"));
|
||||
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/group12"));
|
||||
|
||||
Assert.assertEquals(2, kcGroup1.getSubGroups().size());
|
||||
|
||||
// Create some new groups in keycloak
|
||||
GroupModel model1 = realm.createGroup("model1");
|
||||
realm.moveGroup(model1, null);
|
||||
GroupModel model2 = realm.createGroup("model2");
|
||||
kcGroup1.addChild(model2);
|
||||
|
||||
// Sync groups again from LDAP. Nothing deleted
|
||||
syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, session, realm);
|
||||
LDAPTestUtils.assertSyncEquals(syncResult, 0, 3, 0, 0);
|
||||
|
||||
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/group11"));
|
||||
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/group12"));
|
||||
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/model1"));
|
||||
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/model2"));
|
||||
|
||||
// Update group mapper to drop non-existing groups during sync
|
||||
LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel, GroupMapperConfig.DROP_NON_EXISTING_GROUPS_DURING_SYNC, "true");
|
||||
realm.updateComponent(mapperModel);
|
||||
|
||||
// Sync groups again from LDAP. Assert LDAP non-existing groups deleted
|
||||
syncResult = new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, session, realm);
|
||||
Assert.assertEquals(3, syncResult.getUpdated());
|
||||
Assert.assertTrue(syncResult.getRemoved() == 2);
|
||||
|
||||
// Sync and assert groups updated
|
||||
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/group11"));
|
||||
Assert.assertNotNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/group12"));
|
||||
Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/model1"));
|
||||
Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group1/model2"));
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void test04_syncNoPreserveGroupInheritanceWithLazySync() throws Exception {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
ComponentModel mapperModel = LDAPTestUtils.getComponentByName(realm,ldapModel, "groupsMapper");
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
GroupLDAPStorageMapper groupMapper = LDAPTestUtils.getGroupMapper(mapperModel, ldapProvider, realm);
|
||||
|
||||
// Update group mapper to skip preserve inheritance
|
||||
LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel, GroupMapperConfig.PRESERVE_GROUP_INHERITANCE, "false");
|
||||
realm.updateComponent(mapperModel);
|
||||
|
||||
// Add user to LDAP and put him as member of group11
|
||||
LDAPTestUtils.removeAllLDAPUsers(ldapProvider, realm);
|
||||
LDAPObject johnLdap = LDAPTestUtils.addLDAPUser(ldapProvider, realm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
|
||||
LDAPTestUtils.updateLDAPPassword(ldapProvider, johnLdap, "Password1");
|
||||
groupMapper.addGroupMappingInLDAP("group11", johnLdap);
|
||||
|
||||
// Assert groups not yet imported to Keycloak DB
|
||||
Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group1"));
|
||||
Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group11"));
|
||||
Assert.assertNull(KeycloakModelUtils.findGroupByPath(realm, "/group12"));
|
||||
|
||||
// Load user from LDAP to Keycloak DB
|
||||
UserModel john = session.users().getUserByUsername("johnkeycloak", realm);
|
||||
Set<GroupModel> johnGroups = john.getGroups();
|
||||
|
||||
// Assert just those groups, which john was memberOf exists because they were lazily created
|
||||
GroupModel group1 = KeycloakModelUtils.findGroupByPath(realm, "/group1");
|
||||
GroupModel group11 = KeycloakModelUtils.findGroupByPath(realm, "/group11");
|
||||
GroupModel group12 = KeycloakModelUtils.findGroupByPath(realm, "/group12");
|
||||
Assert.assertNull(group1);
|
||||
Assert.assertNotNull(group11);
|
||||
Assert.assertNull(group12);
|
||||
|
||||
Assert.assertEquals(1, johnGroups.size());
|
||||
Assert.assertTrue(johnGroups.contains(group11));
|
||||
|
||||
// Delete group mapping
|
||||
john.leaveGroup(group11);
|
||||
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
* 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.Test;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.ldap.LDAPConfig;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
||||
import org.keycloak.storage.ldap.LDAPUtils;
|
||||
import org.keycloak.storage.ldap.idm.model.LDAPDn;
|
||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||
import org.keycloak.storage.ldap.mappers.membership.LDAPGroupMapperMode;
|
||||
import org.keycloak.storage.ldap.mappers.membership.MembershipType;
|
||||
import org.keycloak.storage.ldap.mappers.membership.group.GroupLDAPStorageMapper;
|
||||
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.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.LDAPRule;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class LDAPGroupMapperTest {
|
||||
|
||||
private static LDAPRule ldapRule = new LDAPRule();
|
||||
|
||||
private static ComponentModel ldapModel = null;
|
||||
private static String descriptionAttrName = null;
|
||||
|
||||
private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
LDAPTestUtils.addLocalUser(manager.getSession(), appRealm, "mary", "mary@test.com", "password-app");
|
||||
LDAPTestUtils.addLocalUser(manager.getSession(), appRealm, "john", "john@test.com", "password-app");
|
||||
|
||||
MultivaluedHashMap<String,String> ldapConfig = LDAPTestUtils.getLdapRuleConfig(ldapRule);
|
||||
ldapConfig.putSingle(LDAPConstants.SYNC_REGISTRATIONS, "true");
|
||||
ldapConfig.putSingle(LDAPConstants.EDIT_MODE, LDAPStorageProviderFactory.EditMode.WRITABLE.toString());
|
||||
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
|
||||
LDAPObject group1 = LDAPTestUtils.createLDAPGroup(manager.getSession(), appRealm, ldapModel, "group1", descriptionAttrName, "group1 - description");
|
||||
LDAPObject group11 = LDAPTestUtils.createLDAPGroup(manager.getSession(), appRealm, ldapModel, "group11");
|
||||
LDAPObject group12 = LDAPTestUtils.createLDAPGroup(manager.getSession(), appRealm, ldapModel, "group12", descriptionAttrName, "group12 - description");
|
||||
|
||||
LDAPUtils.addMember(ldapFedProvider, MembershipType.DN, LDAPConstants.MEMBER, group1, group11, false);
|
||||
LDAPUtils.addMember(ldapFedProvider, MembershipType.DN, LDAPConstants.MEMBER, group1, group12, true);
|
||||
|
||||
// Sync LDAP groups to Keycloak DB
|
||||
ComponentModel mapperModel = LDAPTestUtils.getComponentByName(appRealm,ldapModel, "groupsMapper");
|
||||
new GroupLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(mapperModel, ldapFedProvider, session, appRealm);
|
||||
|
||||
// Delete all LDAP users
|
||||
LDAPTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm);
|
||||
|
||||
// Add some LDAP users for testing
|
||||
LDAPObject john = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
|
||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, john, "Password1");
|
||||
|
||||
LDAPObject mary = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "marykeycloak", "Mary", "Kelly", "mary@email.org", null, "5678");
|
||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, mary, "Password1");
|
||||
|
||||
LDAPObject rob = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "robkeycloak", "Rob", "Brown", "rob@email.org", null, "8910");
|
||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, rob, "Password1");
|
||||
|
||||
LDAPObject james = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "jameskeycloak", "James", "Brown", "james@email.org", null, "8910");
|
||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, james, "Password1");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@ClassRule
|
||||
public static TestRule chain = RuleChain
|
||||
.outerRule(ldapRule)
|
||||
.around(keycloakRule);
|
||||
|
||||
@Test
|
||||
public void test01_ldapOnlyGroupMappings() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
|
||||
ComponentModel mapperModel = LDAPTestUtils.getComponentByName(appRealm,ldapModel, "groupsMapper");
|
||||
LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel, GroupMapperConfig.MODE, LDAPGroupMapperMode.LDAP_ONLY.toString());
|
||||
appRealm.updateComponent(mapperModel);
|
||||
|
||||
UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
|
||||
UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm);
|
||||
|
||||
// 1 - Grant some groups in LDAP
|
||||
|
||||
// This group should already exists as it was imported from LDAP
|
||||
GroupModel group1 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1");
|
||||
john.joinGroup(group1);
|
||||
|
||||
// This group should already exists as it was imported from LDAP
|
||||
GroupModel group11 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1/group11");
|
||||
mary.joinGroup(group11);
|
||||
|
||||
// This group should already exists as it was imported from LDAP
|
||||
GroupModel group12 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1/group12");
|
||||
john.joinGroup(group12);
|
||||
mary.joinGroup(group12);
|
||||
|
||||
// 2 - Check that group mappings are not in local Keycloak DB (They are in LDAP).
|
||||
|
||||
UserModel johnDb = session.userLocalStorage().getUserByUsername("johnkeycloak", appRealm);
|
||||
Set<GroupModel> johnDbGroups = johnDb.getGroups();
|
||||
Assert.assertEquals(0, johnDbGroups.size());
|
||||
|
||||
// 3 - Check that group mappings are in LDAP and hence available through federation
|
||||
|
||||
Set<GroupModel> johnGroups = john.getGroups();
|
||||
Assert.assertEquals(2, johnGroups.size());
|
||||
Assert.assertTrue(johnGroups.contains(group1));
|
||||
Assert.assertFalse(johnGroups.contains(group11));
|
||||
Assert.assertTrue(johnGroups.contains(group12));
|
||||
|
||||
// 4 - Check through userProvider
|
||||
List<UserModel> group1Members = session.users().getGroupMembers(appRealm, group1, 0, 10);
|
||||
List<UserModel> group11Members = session.users().getGroupMembers(appRealm, group11, 0, 10);
|
||||
List<UserModel> group12Members = session.users().getGroupMembers(appRealm, group12, 0, 10);
|
||||
|
||||
Assert.assertEquals(1, group1Members.size());
|
||||
Assert.assertEquals("johnkeycloak", group1Members.get(0).getUsername());
|
||||
Assert.assertEquals(1, group11Members.size());
|
||||
Assert.assertEquals("marykeycloak", group11Members.get(0).getUsername());
|
||||
Assert.assertEquals(2, group12Members.size());
|
||||
|
||||
// 4 - Delete some group mappings and check they are deleted
|
||||
|
||||
john.leaveGroup(group1);
|
||||
john.leaveGroup(group12);
|
||||
|
||||
mary.leaveGroup(group1);
|
||||
mary.leaveGroup(group12);
|
||||
|
||||
johnGroups = john.getGroups();
|
||||
Assert.assertEquals(0, johnGroups.size());
|
||||
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test02_readOnlyGroupMappings() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
System.out.println("starting test02_readOnlyGroupMappings");
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
|
||||
ComponentModel mapperModel = LDAPTestUtils.getComponentByName(appRealm,ldapModel, "groupsMapper");
|
||||
LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel, GroupMapperConfig.MODE, LDAPGroupMapperMode.READ_ONLY.toString());
|
||||
appRealm.updateComponent(mapperModel);
|
||||
|
||||
UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm);
|
||||
|
||||
GroupModel group1 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1");
|
||||
GroupModel group11 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1/group11");
|
||||
GroupModel group12 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1/group12");
|
||||
|
||||
// Add some group mappings directly into LDAP
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
GroupLDAPStorageMapper groupMapper = LDAPTestUtils.getGroupMapper(mapperModel, ldapProvider, appRealm);
|
||||
|
||||
LDAPObject maryLdap = ldapProvider.loadLDAPUserByUsername(appRealm, "marykeycloak");
|
||||
groupMapper.addGroupMappingInLDAP("group1", maryLdap);
|
||||
groupMapper.addGroupMappingInLDAP("group11", maryLdap);
|
||||
|
||||
// Add some group mapping to model
|
||||
mary.joinGroup(group12);
|
||||
|
||||
// Assert that mary has both LDAP and DB mapped groups
|
||||
Set<GroupModel> maryGroups = mary.getGroups();
|
||||
Assert.assertEquals(3, maryGroups.size());
|
||||
Assert.assertTrue(maryGroups.contains(group1));
|
||||
Assert.assertTrue(maryGroups.contains(group11));
|
||||
Assert.assertTrue(maryGroups.contains(group12));
|
||||
|
||||
// Assert that access through DB will have just DB mapped groups
|
||||
System.out.println("******");
|
||||
UserModel maryDB = session.userLocalStorage().getUserByUsername("marykeycloak", appRealm);
|
||||
Set<GroupModel> maryDBGroups = maryDB.getGroups();
|
||||
Assert.assertFalse(maryDBGroups.contains(group1));
|
||||
Assert.assertFalse(maryDBGroups.contains(group11));
|
||||
Assert.assertTrue(maryDBGroups.contains(group12));
|
||||
|
||||
// Check through userProvider
|
||||
List<UserModel> group1Members = session.users().getGroupMembers(appRealm, group1, 0, 10);
|
||||
List<UserModel> group11Members = session.users().getGroupMembers(appRealm, group11, 0, 10);
|
||||
List<UserModel> group12Members = session.users().getGroupMembers(appRealm, group12, 0, 10);
|
||||
Assert.assertEquals(1, group1Members.size());
|
||||
Assert.assertEquals("marykeycloak", group1Members.get(0).getUsername());
|
||||
Assert.assertEquals(1, group11Members.size());
|
||||
Assert.assertEquals("marykeycloak", group11Members.get(0).getUsername());
|
||||
Assert.assertEquals(1, group12Members.size());
|
||||
Assert.assertEquals("marykeycloak", group12Members.get(0).getUsername());
|
||||
|
||||
mary.leaveGroup(group12);
|
||||
try {
|
||||
mary.leaveGroup(group1);
|
||||
Assert.fail("It wasn't expected to successfully delete LDAP group mappings in READ_ONLY mode");
|
||||
} catch (ModelException expected) {
|
||||
}
|
||||
|
||||
// Delete role mappings directly in LDAP
|
||||
deleteGroupMappingsInLDAP(groupMapper, maryLdap, "group1");
|
||||
deleteGroupMappingsInLDAP(groupMapper, maryLdap, "group11");
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test03_importGroupMappings() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
|
||||
ComponentModel mapperModel = LDAPTestUtils.getComponentByName(appRealm,ldapModel, "groupsMapper");
|
||||
LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel, GroupMapperConfig.MODE, LDAPGroupMapperMode.IMPORT.toString());
|
||||
appRealm.updateComponent(mapperModel);
|
||||
|
||||
// Add some group mappings directly in LDAP
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
GroupLDAPStorageMapper groupMapper = LDAPTestUtils.getGroupMapper(mapperModel, ldapProvider, appRealm);
|
||||
|
||||
LDAPObject robLdap = ldapProvider.loadLDAPUserByUsername(appRealm, "robkeycloak");
|
||||
groupMapper.addGroupMappingInLDAP("group11", robLdap);
|
||||
groupMapper.addGroupMappingInLDAP("group12", robLdap);
|
||||
|
||||
// Get user and check that he has requested groupa from LDAP
|
||||
UserModel rob = session.users().getUserByUsername("robkeycloak", appRealm);
|
||||
Set<GroupModel> robGroups = rob.getGroups();
|
||||
|
||||
GroupModel group1 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1");
|
||||
GroupModel group11 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1/group11");
|
||||
GroupModel group12 = KeycloakModelUtils.findGroupByPath(appRealm, "/group1/group12");
|
||||
|
||||
Assert.assertFalse(robGroups.contains(group1));
|
||||
Assert.assertTrue(robGroups.contains(group11));
|
||||
Assert.assertTrue(robGroups.contains(group12));
|
||||
|
||||
// Delete some group mappings in LDAP and check that it doesn't have any effect and user still has groups
|
||||
deleteGroupMappingsInLDAP(groupMapper, robLdap, "group11");
|
||||
deleteGroupMappingsInLDAP(groupMapper, robLdap, "group12");
|
||||
robGroups = rob.getGroups();
|
||||
Assert.assertTrue(robGroups.contains(group11));
|
||||
Assert.assertTrue(robGroups.contains(group12));
|
||||
|
||||
// Delete group mappings through model and verifies that user doesn't have them anymore
|
||||
rob.leaveGroup(group11);
|
||||
rob.leaveGroup(group12);
|
||||
robGroups = rob.getGroups();
|
||||
Assert.assertEquals(0, robGroups.size());
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// KEYCLOAK-2682
|
||||
@Test
|
||||
public void test04_groupReferencingNonExistentMember() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
// Ignoring this test on ActiveDirectory as it's not allowed to have LDAP group referencing nonexistent member. KEYCLOAK-2682 was related to OpenLDAP TODO: Better solution than programmatic...
|
||||
LDAPConfig config = LDAPTestUtils.getLdapProvider(session, ldapModel).getLdapIdentityStore().getConfig();
|
||||
if (config.isActiveDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
|
||||
ComponentModel mapperModel = LDAPTestUtils.getComponentByName(appRealm,ldapModel, "groupsMapper");
|
||||
LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel, GroupMapperConfig.MODE, LDAPGroupMapperMode.LDAP_ONLY.toString());
|
||||
appRealm.updateComponent(mapperModel);
|
||||
|
||||
// 1 - Add some group to LDAP for testing
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
GroupLDAPStorageMapper groupMapper = LDAPTestUtils.getGroupMapper(mapperModel, ldapProvider, appRealm);
|
||||
LDAPObject group2 = LDAPTestUtils.createLDAPGroup(session, appRealm, ldapModel, "group2", descriptionAttrName, "group2 - description");
|
||||
|
||||
// 2 - Add one existing user rob to LDAP group
|
||||
LDAPObject jamesLdap = ldapProvider.loadLDAPUserByUsername(appRealm, "jameskeycloak");
|
||||
LDAPUtils.addMember(ldapProvider, MembershipType.DN, LDAPConstants.MEMBER, group2, jamesLdap, false);
|
||||
|
||||
// 3 - Add non-existing user to LDAP group
|
||||
LDAPDn nonExistentDn = LDAPDn.fromString(ldapProvider.getLdapIdentityStore().getConfig().getUsersDn());
|
||||
nonExistentDn.addFirst(jamesLdap.getRdnAttributeName(), "nonexistent");
|
||||
LDAPObject nonExistentLdapUser = new LDAPObject();
|
||||
nonExistentLdapUser.setDn(nonExistentDn);
|
||||
LDAPUtils.addMember(ldapProvider, MembershipType.DN, LDAPConstants.MEMBER, group2, nonExistentLdapUser, true);
|
||||
|
||||
// 4 - Check group members. Just existing user rob should be present
|
||||
groupMapper.syncDataFromFederationProviderToKeycloak();
|
||||
GroupModel kcGroup2 = KeycloakModelUtils.findGroupByPath(appRealm, "/group2");
|
||||
List<UserModel> groupUsers = session.users().getGroupMembers(appRealm, kcGroup2, 0, 5);
|
||||
Assert.assertEquals(1, groupUsers.size());
|
||||
UserModel rob = groupUsers.get(0);
|
||||
Assert.assertEquals("jameskeycloak", rob.getUsername());
|
||||
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteGroupMappingsInLDAP(GroupLDAPStorageMapper groupMapper, LDAPObject ldapUser, String groupName) {
|
||||
LDAPObject ldapGroup = groupMapper.loadLDAPGroupByName(groupName);
|
||||
groupMapper.deleteGroupMappingInLDAP(ldapUser, ldapGroup);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* 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.rules.RuleChain;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||
import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.LDAPRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class LDAPMultipleAttributesTest {
|
||||
|
||||
protected String APP_SERVER_BASE_URL = "http://localhost:8081";
|
||||
protected String LOGIN_URL = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(APP_SERVER_BASE_URL + "/auth")).build("test").toString();
|
||||
|
||||
private static LDAPRule ldapRule = new LDAPRule();
|
||||
|
||||
private static ComponentModel ldapModel = null;
|
||||
|
||||
private static 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.EDIT_MODE, LDAPStorageProviderFactory.EditMode.WRITABLE.toString());
|
||||
|
||||
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);
|
||||
|
||||
LDAPTestUtils.addZipCodeLDAPMapper(appRealm, ldapModel);
|
||||
LDAPTestUtils.addUserAttributeMapper(appRealm, ldapModel, "streetMapper", "street", LDAPConstants.STREET);
|
||||
|
||||
// Remove current users and add default users
|
||||
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
LDAPTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm);
|
||||
|
||||
LDAPObject james = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "jbrown", "James", "Brown", "jbrown@keycloak.org", null, "88441");
|
||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, james, "Password1");
|
||||
|
||||
// User for testing duplicating surname and postalCode
|
||||
LDAPObject bruce = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "bwilson", "Bruce", "Wilson", "bwilson@keycloak.org", "Elm 5", "88441", "77332");
|
||||
bruce.setAttribute("sn", new LinkedHashSet<>(Arrays.asList("Wilson", "Schneider")));
|
||||
ldapFedProvider.getLdapIdentityStore().update(bruce);
|
||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, bruce, "Password1");
|
||||
|
||||
// Create ldap-portal client
|
||||
ClientModel ldapClient = KeycloakModelUtils.createClient(appRealm, "ldap-portal");
|
||||
ldapClient.addRedirectUri("/ldap-portal");
|
||||
ldapClient.addRedirectUri("/ldap-portal/*");
|
||||
ldapClient.setManagementUrl("/ldap-portal");
|
||||
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("postalCode", "postal_code", "postal_code", "String", true, "", true, true, true));
|
||||
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("street", "street", "street", "String", true, "", true, true, false));
|
||||
ldapClient.addScopeMapping(appRealm.getRole("user"));
|
||||
ldapClient.setSecret("password");
|
||||
|
||||
// Deploy ldap-portal client
|
||||
URL url = getClass().getResource("/ldap/ldap-app-keycloak.json");
|
||||
keycloakRule.createApplicationDeployment()
|
||||
.name("ldap-portal").contextPath("/ldap-portal")
|
||||
.servletClass(LDAPExampleServlet.class).adapterConfigPath(url.getPath())
|
||||
.role("user").deployApplication();
|
||||
}
|
||||
});
|
||||
|
||||
@ClassRule
|
||||
public static TestRule chain = RuleChain
|
||||
.outerRule(ldapRule)
|
||||
.around(keycloakRule);
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
@WebResource
|
||||
protected OAuthClient oauth;
|
||||
|
||||
@WebResource
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@Test
|
||||
public void testModel() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
|
||||
LDAPTestUtils.assertUserImported(session.users(), appRealm, "jbrown", "James", "Brown", "jbrown@keycloak.org", "88441");
|
||||
|
||||
UserModel user = session.users().getUserByUsername("bwilson", appRealm);
|
||||
Assert.assertEquals("bwilson@keycloak.org", user.getEmail());
|
||||
Assert.assertEquals("Bruce", user.getFirstName());
|
||||
|
||||
// There are 2 lastnames in ldif
|
||||
Assert.assertTrue("Wilson".equals(user.getLastName()) || "Schneider".equals(user.getLastName()));
|
||||
|
||||
// Actually there are 2 postalCodes
|
||||
List<String> postalCodes = user.getAttribute("postal_code");
|
||||
assertPostalCodes(postalCodes, "88441", "77332");
|
||||
List<String> tmp = new LinkedList<>();
|
||||
tmp.addAll(postalCodes);
|
||||
postalCodes = tmp;
|
||||
postalCodes.remove("77332");
|
||||
user.setAttribute("postal_code", postalCodes);
|
||||
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
UserModel user = session.users().getUserByUsername("bwilson", appRealm);
|
||||
List<String> postalCodes = user.getAttribute("postal_code");
|
||||
assertPostalCodes(postalCodes, "88441");
|
||||
List<String> tmp = new LinkedList<>();
|
||||
tmp.addAll(postalCodes);
|
||||
postalCodes = tmp;
|
||||
postalCodes.add("77332");
|
||||
user.setAttribute("postal_code", postalCodes);
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
UserModel user = session.users().getUserByUsername("bwilson", appRealm);
|
||||
assertPostalCodes(user.getAttribute("postal_code"), "88441", "77332");
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertPostalCodes(List<String> postalCodes, String... expectedPostalCodes) {
|
||||
if (expectedPostalCodes == null && postalCodes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Assert.assertEquals(expectedPostalCodes.length, postalCodes.size());
|
||||
for (String expected : expectedPostalCodes) {
|
||||
if (!postalCodes.contains(expected)) {
|
||||
Assert.fail("postalCode '" + expected + "' not in postalCodes: " + postalCodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ldapPortalEndToEndTest() {
|
||||
// Login as bwilson
|
||||
driver.navigate().to(APP_SERVER_BASE_URL + "/ldap-portal");
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
||||
loginPage.login("bwilson", "Password1");
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(APP_SERVER_BASE_URL + "/ldap-portal"));
|
||||
String pageSource = driver.getPageSource();
|
||||
System.out.println(pageSource);
|
||||
Assert.assertTrue(pageSource.contains("bwilson") && pageSource.contains("Bruce"));
|
||||
Assert.assertTrue(pageSource.contains("street") && pageSource.contains("Elm 5"));
|
||||
Assert.assertTrue(pageSource.contains("postal_code") && pageSource.contains("88441") && pageSource.contains("77332"));
|
||||
|
||||
// Logout
|
||||
String logoutUri = OIDCLoginProtocolService.logoutUrl(UriBuilder.fromUri(APP_SERVER_BASE_URL + "/auth"))
|
||||
.queryParam(OAuth2Constants.REDIRECT_URI, APP_SERVER_BASE_URL + "/ldap-portal").build("test").toString();
|
||||
driver.navigate().to(logoutUri);
|
||||
|
||||
// Login as jbrown
|
||||
driver.navigate().to(APP_SERVER_BASE_URL + "/ldap-portal");
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
|
||||
loginPage.login("jbrown", "Password1");
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(APP_SERVER_BASE_URL + "/ldap-portal"));
|
||||
pageSource = driver.getPageSource();
|
||||
System.out.println(pageSource);
|
||||
Assert.assertTrue(pageSource.contains("jbrown") && pageSource.contains("James Brown"));
|
||||
Assert.assertFalse(pageSource.contains("street"));
|
||||
Assert.assertTrue(pageSource.contains("postal_code") && pageSource.contains("88441"));
|
||||
Assert.assertFalse(pageSource.contains("77332"));
|
||||
|
||||
// Logout
|
||||
driver.navigate().to(logoutUri);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -79,16 +79,6 @@ public class LDAPProvidersIntegrationTest {
|
|||
|
||||
private static ComponentModel ldapModel = null;
|
||||
|
||||
private static MultivaluedHashMap<String, String> getLdapRuleConfig() {
|
||||
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
|
||||
Map<String,String> ldapConfig = ldapRule.getConfig();
|
||||
for (Map.Entry<String, String> entry : ldapConfig.entrySet()) {
|
||||
config.add(entry.getKey(), entry.getValue());
|
||||
|
||||
}
|
||||
return config;
|
||||
|
||||
}
|
||||
|
||||
private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
|
@ -96,7 +86,7 @@ public class LDAPProvidersIntegrationTest {
|
|||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
LDAPTestUtils.addLocalUser(manager.getSession(), appRealm, "marykeycloak", "mary@test.com", "password-app");
|
||||
|
||||
MultivaluedHashMap<String,String> ldapConfig = getLdapRuleConfig();
|
||||
MultivaluedHashMap<String,String> ldapConfig = LDAPTestUtils.getLdapRuleConfig(ldapRule);
|
||||
ldapConfig.putSingle(LDAPConstants.SYNC_REGISTRATIONS, "true");
|
||||
ldapConfig.putSingle(LDAPConstants.EDIT_MODE, LDAPStorageProviderFactory.EditMode.WRITABLE.toString());
|
||||
UserStorageProviderModel model = new UserStorageProviderModel();
|
||||
|
@ -441,7 +431,7 @@ public class LDAPProvidersIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
//@Test // don't think we should support this, bburke
|
||||
public void testDirectLDAPUpdate() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
|
||||
|
@ -728,6 +718,17 @@ public class LDAPProvidersIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void testRemoveFederatedUser() {
|
||||
/*
|
||||
{
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
UserModel user = session.users().getUserByUsername("registerUserSuccess2", appRealm);
|
||||
keycloakRule.stopSession(session, true);
|
||||
if (user == null) {
|
||||
registerUserLdapSuccess();
|
||||
}
|
||||
}
|
||||
*/
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
|
@ -756,26 +757,26 @@ public class LDAPProvidersIntegrationTest {
|
|||
LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "username4", "John4", "Doel4", "user4@email.org", null, "124");
|
||||
|
||||
// Users are not at local store at this moment
|
||||
Assert.assertNull(session.userStorage().getUserByUsername("username1", appRealm));
|
||||
Assert.assertNull(session.userStorage().getUserByUsername("username2", appRealm));
|
||||
Assert.assertNull(session.userStorage().getUserByUsername("username3", appRealm));
|
||||
Assert.assertNull(session.userStorage().getUserByUsername("username4", appRealm));
|
||||
Assert.assertNull(session.userLocalStorage().getUserByUsername("username1", appRealm));
|
||||
Assert.assertNull(session.userLocalStorage().getUserByUsername("username2", appRealm));
|
||||
Assert.assertNull(session.userLocalStorage().getUserByUsername("username3", appRealm));
|
||||
Assert.assertNull(session.userLocalStorage().getUserByUsername("username4", appRealm));
|
||||
|
||||
// search by username
|
||||
session.users().searchForUser("username1", appRealm);
|
||||
LDAPTestUtils.assertUserImported(session.userStorage(), appRealm, "username1", "John1", "Doel1", "user1@email.org", "121");
|
||||
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username1", "John1", "Doel1", "user1@email.org", "121");
|
||||
|
||||
// search by email
|
||||
session.users().searchForUser("user2@email.org", appRealm);
|
||||
LDAPTestUtils.assertUserImported(session.userStorage(), appRealm, "username2", "John2", "Doel2", "user2@email.org", "122");
|
||||
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username2", "John2", "Doel2", "user2@email.org", "122");
|
||||
|
||||
// search by lastName
|
||||
session.users().searchForUser("Doel3", appRealm);
|
||||
LDAPTestUtils.assertUserImported(session.userStorage(), appRealm, "username3", "John3", "Doel3", "user3@email.org", "123");
|
||||
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username3", "John3", "Doel3", "user3@email.org", "123");
|
||||
|
||||
// search by firstName + lastName
|
||||
session.users().searchForUser("John4 Doel4", appRealm);
|
||||
LDAPTestUtils.assertUserImported(session.userStorage(), appRealm, "username4", "John4", "Doel4", "user4@email.org", "124");
|
||||
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username4", "John4", "Doel4", "user4@email.org", "124");
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
|
@ -803,15 +804,15 @@ public class LDAPProvidersIntegrationTest {
|
|||
LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "username7", "John7", "Doel7", "user7@email.org", null, "127");
|
||||
|
||||
// search by email
|
||||
session.users().searchForUser("user5@email.org", appRealm);
|
||||
LDAPTestUtils.assertUserImported(session.userStorage(), appRealm, "username5", "John5", "Doel5", "user5@email.org", "125");
|
||||
List<UserModel> list = session.users().searchForUser("user5@email.org", appRealm);
|
||||
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username5", "John5", "Doel5", "user5@email.org", "125");
|
||||
|
||||
session.users().searchForUser("John6 Doel6", appRealm);
|
||||
LDAPTestUtils.assertUserImported(session.userStorage(), appRealm, "username6", "John6", "Doel6", "user6@email.org", "126");
|
||||
LDAPTestUtils.assertUserImported(session.userLocalStorage(), appRealm, "username6", "John6", "Doel6", "user6@email.org", "126");
|
||||
|
||||
session.users().searchForUser("user7@email.org", appRealm);
|
||||
session.users().searchForUser("John7 Doel7", appRealm);
|
||||
Assert.assertNull(session.userStorage().getUserByUsername("username7", appRealm));
|
||||
Assert.assertNull(session.userLocalStorage().getUserByUsername("username7", appRealm));
|
||||
|
||||
// Remove custom filter
|
||||
ldapModel.getConfig().remove(LDAPConstants.CUSTOM_USER_SEARCH_FILTER);
|
||||
|
@ -850,7 +851,7 @@ public class LDAPProvidersIntegrationTest {
|
|||
Assert.assertTrue(session.users().removeUser(appRealm, user));
|
||||
|
||||
// Assert user not available locally, but will be reimported from LDAP once searched
|
||||
Assert.assertNull(session.userStorage().getUserByUsername("johnkeycloak", appRealm));
|
||||
Assert.assertNull(session.userLocalStorage().getUserByUsername("johnkeycloak", appRealm));
|
||||
Assert.assertNotNull(session.users().getUserByUsername("johnkeycloak", appRealm));
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
|
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* 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.rules.RuleChain;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||
import org.keycloak.storage.ldap.mappers.membership.LDAPGroupMapperMode;
|
||||
import org.keycloak.models.AccountRoles;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.storage.ldap.mappers.membership.role.RoleLDAPStorageMapper;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.LDAPRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class LDAPRoleMappingsTest {
|
||||
|
||||
private static LDAPRule ldapRule = new LDAPRule();
|
||||
|
||||
private static ComponentModel ldapModel = null;
|
||||
|
||||
private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
LDAPTestUtils.addLocalUser(manager.getSession(), appRealm, "mary", "mary@test.com", "password-app");
|
||||
|
||||
MultivaluedHashMap<String,String> ldapConfig = LDAPTestUtils.getLdapRuleConfig(ldapRule);
|
||||
ldapConfig.putSingle(LDAPConstants.SYNC_REGISTRATIONS, "true");
|
||||
ldapConfig.putSingle(LDAPConstants.EDIT_MODE, LDAPStorageProviderFactory.EditMode.WRITABLE.toString());
|
||||
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);
|
||||
|
||||
// Delete all LDAP users
|
||||
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
LDAPTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm);
|
||||
|
||||
// Add sample application
|
||||
ClientModel finance = appRealm.addClient("finance");
|
||||
|
||||
// Delete all LDAP roles
|
||||
LDAPTestUtils.addOrUpdateRoleLDAPMappers(appRealm, ldapModel, LDAPGroupMapperMode.LDAP_ONLY);
|
||||
LDAPTestUtils.removeAllLDAPRoles(manager.getSession(), appRealm, ldapModel, "realmRolesMapper");
|
||||
LDAPTestUtils.removeAllLDAPRoles(manager.getSession(), appRealm, ldapModel, "financeRolesMapper");
|
||||
|
||||
// Add some users for testing
|
||||
LDAPObject john = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
|
||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, john, "Password1");
|
||||
|
||||
LDAPObject mary = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "marykeycloak", "Mary", "Kelly", "mary@email.org", null, "5678");
|
||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, mary, "Password1");
|
||||
|
||||
LDAPObject rob = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "robkeycloak", "Rob", "Brown", "rob@email.org", null, "8910");
|
||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, rob, "Password1");
|
||||
|
||||
// Add some roles for testing
|
||||
LDAPTestUtils.createLDAPRole(manager.getSession(), appRealm, ldapModel, "realmRolesMapper", "realmRole1");
|
||||
LDAPTestUtils.createLDAPRole(manager.getSession(), appRealm, ldapModel, "realmRolesMapper", "realmRole2");
|
||||
LDAPTestUtils.createLDAPRole(manager.getSession(), appRealm, ldapModel, "financeRolesMapper", "financeRole1");
|
||||
|
||||
// Sync LDAP roles to Keycloak DB
|
||||
LDAPTestUtils.syncRolesFromLDAP(appRealm, ldapFedProvider, ldapModel);
|
||||
}
|
||||
});
|
||||
|
||||
@ClassRule
|
||||
public static TestRule chain = RuleChain
|
||||
.outerRule(ldapRule)
|
||||
.around(keycloakRule);
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@WebResource
|
||||
protected OAuthClient oauth;
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
@WebResource
|
||||
protected AppPage appPage;
|
||||
|
||||
@WebResource
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@Test
|
||||
public void test01_ldapOnlyRoleMappings() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
|
||||
LDAPTestUtils.addOrUpdateRoleLDAPMappers(appRealm, ldapModel, LDAPGroupMapperMode.LDAP_ONLY);
|
||||
|
||||
UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
|
||||
UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm);
|
||||
|
||||
// 1 - Grant some roles in LDAP
|
||||
|
||||
// This role should already exists as it was imported from LDAP
|
||||
RoleModel realmRole1 = appRealm.getRole("realmRole1");
|
||||
john.grantRole(realmRole1);
|
||||
|
||||
// This role should already exists as it was imported from LDAP
|
||||
RoleModel realmRole2 = appRealm.getRole("realmRole2");
|
||||
mary.grantRole(realmRole2);
|
||||
|
||||
// This role may already exists from previous test (was imported from LDAP), but may not
|
||||
RoleModel realmRole3 = appRealm.getRole("realmRole3");
|
||||
if (realmRole3 == null) {
|
||||
realmRole3 = appRealm.addRole("realmRole3");
|
||||
}
|
||||
|
||||
john.grantRole(realmRole3);
|
||||
mary.grantRole(realmRole3);
|
||||
|
||||
ClientModel accountApp = appRealm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
|
||||
ClientModel financeApp = appRealm.getClientByClientId("finance");
|
||||
|
||||
RoleModel manageAccountRole = accountApp.getRole(AccountRoles.MANAGE_ACCOUNT);
|
||||
RoleModel financeRole1 = financeApp.getRole("financeRole1");
|
||||
john.grantRole(financeRole1);
|
||||
|
||||
// 2 - Check that role mappings are not in local Keycloak DB (They are in LDAP).
|
||||
|
||||
UserModel johnDb = session.userLocalStorage().getUserByUsername("johnkeycloak", appRealm);
|
||||
Set<RoleModel> johnDbRoles = johnDb.getRoleMappings();
|
||||
Assert.assertFalse(johnDbRoles.contains(realmRole1));
|
||||
Assert.assertFalse(johnDbRoles.contains(realmRole2));
|
||||
Assert.assertFalse(johnDbRoles.contains(realmRole3));
|
||||
Assert.assertFalse(johnDbRoles.contains(financeRole1));
|
||||
Assert.assertTrue(johnDbRoles.contains(manageAccountRole));
|
||||
|
||||
// 3 - Check that role mappings are in LDAP and hence available through federation
|
||||
|
||||
Set<RoleModel> johnRoles = john.getRoleMappings();
|
||||
Assert.assertTrue(johnRoles.contains(realmRole1));
|
||||
Assert.assertFalse(johnRoles.contains(realmRole2));
|
||||
Assert.assertTrue(johnRoles.contains(realmRole3));
|
||||
Assert.assertTrue(johnRoles.contains(financeRole1));
|
||||
Assert.assertTrue(johnRoles.contains(manageAccountRole));
|
||||
|
||||
Set<RoleModel> johnRealmRoles = john.getRealmRoleMappings();
|
||||
Assert.assertEquals(2, johnRealmRoles.size());
|
||||
Assert.assertTrue(johnRealmRoles.contains(realmRole1));
|
||||
Assert.assertTrue(johnRealmRoles.contains(realmRole3));
|
||||
|
||||
// account roles are not mapped in LDAP. Those are in Keycloak DB
|
||||
Set<RoleModel> johnAccountRoles = john.getClientRoleMappings(accountApp);
|
||||
Assert.assertTrue(johnAccountRoles.contains(manageAccountRole));
|
||||
|
||||
Set<RoleModel> johnFinanceRoles = john.getClientRoleMappings(financeApp);
|
||||
Assert.assertEquals(1, johnFinanceRoles.size());
|
||||
Assert.assertTrue(johnFinanceRoles.contains(financeRole1));
|
||||
|
||||
// 4 - Delete some role mappings and check they are deleted
|
||||
|
||||
john.deleteRoleMapping(realmRole3);
|
||||
john.deleteRoleMapping(realmRole1);
|
||||
john.deleteRoleMapping(financeRole1);
|
||||
john.deleteRoleMapping(manageAccountRole);
|
||||
|
||||
johnRoles = john.getRoleMappings();
|
||||
Assert.assertFalse(johnRoles.contains(realmRole1));
|
||||
Assert.assertFalse(johnRoles.contains(realmRole2));
|
||||
Assert.assertFalse(johnRoles.contains(realmRole3));
|
||||
Assert.assertFalse(johnRoles.contains(financeRole1));
|
||||
Assert.assertFalse(johnRoles.contains(manageAccountRole));
|
||||
|
||||
// Cleanup
|
||||
mary.deleteRoleMapping(realmRole2);
|
||||
mary.deleteRoleMapping(realmRole3);
|
||||
john.grantRole(manageAccountRole);
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test02_readOnlyRoleMappings() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
|
||||
LDAPTestUtils.addOrUpdateRoleLDAPMappers(appRealm, ldapModel, LDAPGroupMapperMode.READ_ONLY);
|
||||
|
||||
UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm);
|
||||
|
||||
RoleModel realmRole1 = appRealm.getRole("realmRole1");
|
||||
RoleModel realmRole2 = appRealm.getRole("realmRole2");
|
||||
RoleModel realmRole3 = appRealm.getRole("realmRole3");
|
||||
if (realmRole3 == null) {
|
||||
realmRole3 = appRealm.addRole("realmRole3");
|
||||
}
|
||||
|
||||
// Add some role mappings directly into LDAP
|
||||
ComponentModel roleMapperModel = LDAPTestUtils.getComponentByName(appRealm, ldapModel, "realmRolesMapper");
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
RoleLDAPStorageMapper roleMapper = LDAPTestUtils.getRoleMapper(roleMapperModel, ldapProvider, appRealm);
|
||||
|
||||
LDAPObject maryLdap = ldapProvider.loadLDAPUserByUsername(appRealm, "marykeycloak");
|
||||
roleMapper.addRoleMappingInLDAP("realmRole1", maryLdap);
|
||||
roleMapper.addRoleMappingInLDAP("realmRole2", maryLdap);
|
||||
|
||||
// Add some role to model
|
||||
mary.grantRole(realmRole3);
|
||||
|
||||
// Assert that mary has both LDAP and DB mapped roles
|
||||
Set<RoleModel> maryRoles = mary.getRealmRoleMappings();
|
||||
Assert.assertTrue(maryRoles.contains(realmRole1));
|
||||
Assert.assertTrue(maryRoles.contains(realmRole2));
|
||||
Assert.assertTrue(maryRoles.contains(realmRole3));
|
||||
|
||||
// Assert that access through DB will have just DB mapped role
|
||||
UserModel maryDB = session.userLocalStorage().getUserByUsername("marykeycloak", appRealm);
|
||||
Set<RoleModel> maryDBRoles = maryDB.getRealmRoleMappings();
|
||||
Assert.assertFalse(maryDBRoles.contains(realmRole1));
|
||||
Assert.assertFalse(maryDBRoles.contains(realmRole2));
|
||||
Assert.assertTrue(maryDBRoles.contains(realmRole3));
|
||||
|
||||
mary.deleteRoleMapping(realmRole3);
|
||||
try {
|
||||
mary.deleteRoleMapping(realmRole1);
|
||||
Assert.fail("It wasn't expected to successfully delete LDAP role mappings in READ_ONLY mode");
|
||||
} catch (ModelException expected) {
|
||||
}
|
||||
|
||||
// Delete role mappings directly in LDAP
|
||||
deleteRoleMappingsInLDAP(roleMapper, maryLdap, "realmRole1");
|
||||
deleteRoleMappingsInLDAP(roleMapper, maryLdap, "realmRole2");
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm);
|
||||
|
||||
// Assert role mappings is not available
|
||||
Set<RoleModel> maryRoles = mary.getRealmRoleMappings();
|
||||
Assert.assertFalse(maryRoles.contains(appRealm.getRole("realmRole1")));
|
||||
Assert.assertFalse(maryRoles.contains(appRealm.getRole("realmRole2")));
|
||||
Assert.assertFalse(maryRoles.contains(appRealm.getRole("realmRole3")));
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test03_importRoleMappings() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
|
||||
LDAPTestUtils.addOrUpdateRoleLDAPMappers(appRealm, ldapModel, LDAPGroupMapperMode.IMPORT);
|
||||
|
||||
// Add some role mappings directly in LDAP
|
||||
ComponentModel roleMapperModel = LDAPTestUtils.getComponentByName(appRealm, ldapModel, "realmRolesMapper");
|
||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
RoleLDAPStorageMapper roleMapper = LDAPTestUtils.getRoleMapper(roleMapperModel, ldapProvider, appRealm);
|
||||
|
||||
LDAPObject robLdap = ldapProvider.loadLDAPUserByUsername(appRealm, "robkeycloak");
|
||||
roleMapper.addRoleMappingInLDAP("realmRole1", robLdap);
|
||||
roleMapper.addRoleMappingInLDAP("realmRole2", robLdap);
|
||||
|
||||
// Get user and check that he has requested roles from LDAP
|
||||
UserModel rob = session.users().getUserByUsername("robkeycloak", appRealm);
|
||||
RoleModel realmRole1 = appRealm.getRole("realmRole1");
|
||||
RoleModel realmRole2 = appRealm.getRole("realmRole2");
|
||||
RoleModel realmRole3 = appRealm.getRole("realmRole3");
|
||||
if (realmRole3 == null) {
|
||||
realmRole3 = appRealm.addRole("realmRole3");
|
||||
}
|
||||
Set<RoleModel> robRoles = rob.getRealmRoleMappings();
|
||||
Assert.assertTrue(robRoles.contains(realmRole1));
|
||||
Assert.assertTrue(robRoles.contains(realmRole2));
|
||||
Assert.assertFalse(robRoles.contains(realmRole3));
|
||||
|
||||
// Add some role mappings in model and check that user has it
|
||||
rob.grantRole(realmRole3);
|
||||
robRoles = rob.getRealmRoleMappings();
|
||||
Assert.assertTrue(robRoles.contains(realmRole3));
|
||||
|
||||
// Delete some role mappings in LDAP and check that it doesn't have any effect and user still has role
|
||||
deleteRoleMappingsInLDAP(roleMapper, robLdap, "realmRole1");
|
||||
deleteRoleMappingsInLDAP(roleMapper, robLdap, "realmRole2");
|
||||
robRoles = rob.getRealmRoleMappings();
|
||||
Assert.assertTrue(robRoles.contains(realmRole1));
|
||||
Assert.assertTrue(robRoles.contains(realmRole2));
|
||||
|
||||
// Delete role mappings through model and verifies that user doesn't have them anymore
|
||||
rob.deleteRoleMapping(realmRole1);
|
||||
rob.deleteRoleMapping(realmRole2);
|
||||
rob.deleteRoleMapping(realmRole3);
|
||||
robRoles = rob.getRealmRoleMappings();
|
||||
Assert.assertFalse(robRoles.contains(realmRole1));
|
||||
Assert.assertFalse(robRoles.contains(realmRole2));
|
||||
Assert.assertFalse(robRoles.contains(realmRole3));
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteRoleMappingsInLDAP(RoleLDAPStorageMapper roleMapper, LDAPObject ldapUser, String roleName) {
|
||||
LDAPObject ldapRole1 = roleMapper.loadLDAPRoleByName(roleName);
|
||||
roleMapper.deleteRoleMappingInLDAP(ldapUser, ldapRole1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* 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.Test;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.services.managers.UserStorageSyncManager;
|
||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
||||
import org.keycloak.storage.user.SynchronizationResult;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.LDAPRule;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class LDAPSyncTest {
|
||||
|
||||
private static LDAPRule ldapRule = new LDAPRule();
|
||||
|
||||
private static UserStorageProviderModel ldapModel = null;
|
||||
|
||||
private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
// Other tests may left Time offset uncleared, which could cause issues
|
||||
Time.setOffset(0);
|
||||
|
||||
MultivaluedHashMap<String,String> ldapConfig = LDAPTestUtils.getLdapRuleConfig(ldapRule);
|
||||
ldapConfig.putSingle(LDAPConstants.SYNC_REGISTRATIONS, "false");
|
||||
ldapConfig.putSingle(LDAPConstants.EDIT_MODE, LDAPStorageProviderFactory.EditMode.WRITABLE.toString());
|
||||
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 = new UserStorageProviderModel(appRealm.addComponentModel(model));
|
||||
|
||||
|
||||
LDAPTestUtils.addZipCodeLDAPMapper(appRealm, ldapModel);
|
||||
|
||||
// Delete all LDAP users and add 5 new users for testing
|
||||
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
LDAPTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm);
|
||||
|
||||
for (int i=1 ; i<=5 ; i++) {
|
||||
LDAPObject ldapUser = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "user" + i, "User" + i + "FN", "User" + i + "LN", "user" + i + "@email.org", null, "12" + i);
|
||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, ldapUser, "Password1");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ClassRule
|
||||
public static TestRule chain = RuleChain
|
||||
.outerRule(ldapRule)
|
||||
.around(keycloakRule);
|
||||
|
||||
// @Test
|
||||
// public void test01runit() throws Exception {
|
||||
// Thread.sleep(10000000);
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void test01LDAPSync() {
|
||||
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
|
||||
|
||||
// wait a bit
|
||||
sleep(ldapRule.getSleepTime());
|
||||
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
SynchronizationResult syncResult = usersSyncManager.syncAllUsers(sessionFactory, "test", ldapModel);
|
||||
LDAPTestUtils.assertSyncEquals(syncResult, 5, 0, 0, 0);
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
UserProvider userProvider = session.userLocalStorage();
|
||||
// Assert users imported
|
||||
LDAPTestUtils.assertUserImported(userProvider, testRealm, "user1", "User1FN", "User1LN", "user1@email.org", "121");
|
||||
LDAPTestUtils.assertUserImported(userProvider, testRealm, "user2", "User2FN", "User2LN", "user2@email.org", "122");
|
||||
LDAPTestUtils.assertUserImported(userProvider, testRealm, "user3", "User3FN", "User3LN", "user3@email.org", "123");
|
||||
LDAPTestUtils.assertUserImported(userProvider, testRealm, "user4", "User4FN", "User4LN", "user4@email.org", "124");
|
||||
LDAPTestUtils.assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5@email.org", "125");
|
||||
|
||||
// Assert lastSync time updated
|
||||
Assert.assertTrue(ldapModel.getLastSync() > 0);
|
||||
for (UserStorageProviderModel persistentFedModel : testRealm.getUserStorageProviders()) {
|
||||
if (LDAPStorageProviderFactory.PROVIDER_NAME.equals(persistentFedModel.getProviderId())) {
|
||||
Assert.assertTrue(persistentFedModel.getLastSync() > 0);
|
||||
} else {
|
||||
// Dummy provider has still 0
|
||||
Assert.assertEquals(0, persistentFedModel.getLastSync());
|
||||
}
|
||||
}
|
||||
|
||||
// wait a bit
|
||||
sleep(ldapRule.getSleepTime());
|
||||
|
||||
// Add user to LDAP and update 'user5' in LDAP
|
||||
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
LDAPTestUtils.addLDAPUser(ldapFedProvider, testRealm, "user6", "User6FN", "User6LN", "user6@email.org", null, "126");
|
||||
LDAPObject ldapUser5 = ldapFedProvider.loadLDAPUserByUsername(testRealm, "user5");
|
||||
// NOTE: Changing LDAP attributes directly here
|
||||
ldapUser5.setSingleAttribute(LDAPConstants.EMAIL, "user5Updated@email.org");
|
||||
ldapUser5.setSingleAttribute(LDAPConstants.POSTAL_CODE, "521");
|
||||
ldapFedProvider.getLdapIdentityStore().update(ldapUser5);
|
||||
|
||||
// Assert still old users in local provider
|
||||
LDAPTestUtils.assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5@email.org", "125");
|
||||
Assert.assertNull(userProvider.getUserByUsername("user6", testRealm));
|
||||
|
||||
// Trigger partial sync
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
SynchronizationResult syncResult = usersSyncManager.syncChangedUsers(sessionFactory, "test", ldapModel);
|
||||
LDAPTestUtils.assertSyncEquals(syncResult, 1, 1, 0, 0);
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
UserProvider userProvider = session.userLocalStorage();
|
||||
// Assert users updated in local provider
|
||||
LDAPTestUtils.assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5updated@email.org", "521");
|
||||
LDAPTestUtils.assertUserImported(userProvider, testRealm, "user6", "User6FN", "User6LN", "user6@email.org", "126");
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test02duplicateUsernameAndEmailSync() {
|
||||
LDAPObject duplicatedLdapUser;
|
||||
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
|
||||
LDAPTestUtils.addLocalUser(session, testRealm, "user7", "user7@email.org", "password");
|
||||
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
|
||||
// Add user to LDAP with duplicated username "user7"
|
||||
duplicatedLdapUser = LDAPTestUtils.addLDAPUser(ldapFedProvider, testRealm, "user7", "User7FN", "User7LN", "user7-something@email.org", null, "126");
|
||||
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
|
||||
// Assert syncing from LDAP fails due to duplicated username
|
||||
SynchronizationResult result = new UserStorageSyncManager().syncAllUsers(session.getKeycloakSessionFactory(), "test", ldapModel);
|
||||
Assert.assertEquals(1, result.getFailed());
|
||||
|
||||
// Remove "user7" from LDAP
|
||||
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
ldapFedProvider.getLdapIdentityStore().remove(duplicatedLdapUser);
|
||||
|
||||
// Add user to LDAP with duplicated email "user7@email.org"
|
||||
duplicatedLdapUser = LDAPTestUtils.addLDAPUser(ldapFedProvider, testRealm, "user7-something", "User7FNN", "User7LNL", "user7@email.org", null, "126");
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
|
||||
// Assert syncing from LDAP fails due to duplicated email
|
||||
SynchronizationResult result = new UserStorageSyncManager().syncAllUsers(session.getKeycloakSessionFactory(), "test", ldapModel);
|
||||
Assert.assertEquals(1, result.getFailed());
|
||||
Assert.assertNull(session.userLocalStorage().getUserByUsername("user7-something", testRealm));
|
||||
|
||||
// Update LDAP user to avoid duplicated email
|
||||
duplicatedLdapUser.setSingleAttribute(LDAPConstants.EMAIL, "user7-changed@email.org");
|
||||
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
ldapFedProvider.getLdapIdentityStore().update(duplicatedLdapUser);
|
||||
|
||||
// Assert user successfully synced now
|
||||
result = new UserStorageSyncManager().syncAllUsers(session.getKeycloakSessionFactory(), "test", ldapModel);
|
||||
Assert.assertEquals(0, result.getFailed());
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
|
||||
// Assert user imported in another transaction
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
LDAPTestUtils.assertUserImported(session.userLocalStorage(), testRealm, "user7-something", "User7FNN", "User7LNL", "user7-changed@email.org", "126");
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
// KEYCLOAK-1571
|
||||
@Test
|
||||
public void test03SameUUIDAndUsernameSync() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
String origUuidAttrName;
|
||||
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
|
||||
// Remove all users from model
|
||||
for (UserModel user : session.userLocalStorage().getUsers(testRealm, true)) {
|
||||
session.userLocalStorage().removeUser(testRealm, user);
|
||||
}
|
||||
|
||||
UserStorageProviderModel providerModel = KeycloakModelUtils.findUserStorageProviderByName(ldapModel.getName(), testRealm);
|
||||
|
||||
// Change name of UUID attribute to same like usernameAttribute
|
||||
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
String uidAttrName = ldapFedProvider.getLdapIdentityStore().getConfig().getUsernameLdapAttribute();
|
||||
origUuidAttrName = providerModel.getConfig().getFirst(LDAPConstants.UUID_LDAP_ATTRIBUTE);
|
||||
providerModel.getConfig().putSingle(LDAPConstants.UUID_LDAP_ATTRIBUTE, uidAttrName);
|
||||
|
||||
// Need to change this due to ApacheDS pagination bug (For other LDAP servers, pagination works fine) TODO: Remove once ApacheDS upgraded and pagination is fixed
|
||||
providerModel.getConfig().putSingle(LDAPConstants.BATCH_SIZE_FOR_SYNC, "10");
|
||||
testRealm.updateComponent(providerModel);
|
||||
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
UserStorageProviderModel providerModel = KeycloakModelUtils.findUserStorageProviderByName(ldapModel.getName(), testRealm);
|
||||
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
SynchronizationResult syncResult = new UserStorageSyncManager().syncAllUsers(sessionFactory, "test", providerModel);
|
||||
Assert.assertEquals(0, syncResult.getFailed());
|
||||
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
|
||||
// Assert users imported with correct LDAP_ID
|
||||
LDAPTestUtils.assertUserImported(session.users(), testRealm, "user1", "User1FN", "User1LN", "user1@email.org", "121");
|
||||
LDAPTestUtils.assertUserImported(session.users(), testRealm, "user2", "User2FN", "User2LN", "user2@email.org", "122");
|
||||
UserModel user1 = session.users().getUserByUsername("user1", testRealm);
|
||||
Assert.assertEquals("user1", user1.getFirstAttribute(LDAPConstants.LDAP_ID));
|
||||
|
||||
// Revert config changes
|
||||
UserStorageProviderModel providerModel = KeycloakModelUtils.findUserStorageProviderByName(ldapModel.getName(), testRealm);
|
||||
providerModel.getConfig().putSingle(LDAPConstants.UUID_LDAP_ATTRIBUTE, origUuidAttrName);
|
||||
testRealm.updateComponent(providerModel);
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
}
|
||||
|
||||
// KEYCLOAK-1728
|
||||
@Test
|
||||
public void test04MissingLDAPUsernameSync() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
String origUsernameAttrName;
|
||||
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
|
||||
// Remove all users from model
|
||||
for (UserModel user : session.userLocalStorage().getUsers(testRealm, true)) {
|
||||
System.out.println("trying to delete user: " + user.getUsername());
|
||||
session.getUserCache().evict(testRealm, user);
|
||||
session.userLocalStorage().removeUser(testRealm, user);
|
||||
}
|
||||
|
||||
UserStorageProviderModel providerModel = KeycloakModelUtils.findUserStorageProviderByName(ldapModel.getName(), testRealm);
|
||||
|
||||
// Add street mapper and add some user including street
|
||||
ComponentModel streetMapper = LDAPTestUtils.addUserAttributeMapper(testRealm, ldapModel, "streetMapper", "street", LDAPConstants.STREET);
|
||||
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||
LDAPObject streetUser = LDAPTestUtils.addLDAPUser(ldapFedProvider, testRealm, "user8", "User8FN", "User8LN", "user8@email.org", "user8street", "126");
|
||||
|
||||
// Change name of username attribute name to street
|
||||
origUsernameAttrName = providerModel.getConfig().getFirst(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
|
||||
providerModel.getConfig().putSingle(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, "street");
|
||||
|
||||
// Need to change this due to ApacheDS pagination bug (For other LDAP servers, pagination works fine) TODO: Remove once ApacheDS upgraded and pagination is fixed
|
||||
providerModel.getConfig().putSingle(LDAPConstants.BATCH_SIZE_FOR_SYNC, "10");
|
||||
testRealm.updateComponent(providerModel);
|
||||
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
|
||||
// Just user8 synced. All others failed to sync
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
UserStorageProviderModel providerModel = KeycloakModelUtils.findUserStorageProviderByName(ldapModel.getName(), testRealm);
|
||||
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
SynchronizationResult syncResult = new UserStorageSyncManager().syncAllUsers(sessionFactory, "test", providerModel);
|
||||
Assert.assertEquals(1, syncResult.getAdded());
|
||||
Assert.assertTrue(syncResult.getFailed() > 0);
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
|
||||
// Revert config changes
|
||||
UserStorageProviderModel providerModel = KeycloakModelUtils.findUserStorageProviderByName(ldapModel.getName(), testRealm);
|
||||
providerModel.getConfig().putSingle(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, origUsernameAttrName);
|
||||
testRealm.updateComponent(providerModel);
|
||||
ComponentModel streetMapper = LDAPTestUtils.getComponentByName(testRealm, providerModel, "streetMapper");
|
||||
testRealm.removeComponent(streetMapper);
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void sleep(int time) {
|
||||
try {
|
||||
Thread.sleep(time);
|
||||
} catch (InterruptedException ie) {
|
||||
throw new RuntimeException(ie);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
package org.keycloak.testsuite.federation.storage.ldap;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
|
@ -46,6 +47,7 @@ import org.keycloak.storage.ldap.mappers.membership.role.RoleLDAPStorageMapper;
|
|||
import org.keycloak.storage.ldap.mappers.membership.role.RoleLDAPStorageMapperFactory;
|
||||
import org.keycloak.storage.ldap.mappers.membership.role.RoleMapperConfig;
|
||||
import org.keycloak.storage.user.SynchronizationResult;
|
||||
import org.keycloak.testsuite.rule.LDAPRule;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -58,9 +60,20 @@ import java.util.Set;
|
|||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class LDAPTestUtils {
|
||||
public static MultivaluedHashMap<String, String> getLdapRuleConfig(LDAPRule ldapRule) {
|
||||
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
|
||||
Map<String,String> ldapConfig = ldapRule.getConfig();
|
||||
for (Map.Entry<String, String> entry : ldapConfig.entrySet()) {
|
||||
config.add(entry.getKey(), entry.getValue());
|
||||
|
||||
}
|
||||
return config;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static UserModel addLocalUser(KeycloakSession session, RealmModel realm, String username, String email, String password) {
|
||||
UserModel user = session.userStorage().addUser(realm, username);
|
||||
UserModel user = session.userLocalStorage().addUser(realm, username);
|
||||
user.setEmail(email);
|
||||
user.setEnabled(true);
|
||||
|
||||
|
|
Loading…
Reference in a new issue