ldap port

This commit is contained in:
Bill Burke 2016-11-05 20:04:53 -04:00
parent c75dcb90c2
commit 4302b440ee
21 changed files with 2063 additions and 35 deletions

View file

@ -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);

View file

@ -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);
}

View file

@ -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

View file

@ -63,6 +63,11 @@ public class UserAdapter implements CachedUserModel {
return updated;
}
@Override
public boolean isMarkedForEviction() {
return updated != null;
}
@Override
public void invalidate() {
getDelegateForUpdate();

View file

@ -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) {

View file

@ -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());

View file

@ -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);

View file

@ -35,6 +35,8 @@ public interface CachedUserModel extends UserModel {
*/
UserModel getDelegateForUpdate();
boolean isMarkedForEviction();
/**
* Invalidate the cache for this model
*

View file

@ -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();
}

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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());

View file

@ -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"));
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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);