From 490b3e36031cd989e7d3d68a37cddb94c5fbd7d9 Mon Sep 17 00:00:00 2001 From: mposolda Date: Fri, 22 May 2015 15:04:28 +0200 Subject: [PATCH] KEYCLOAK-886 Added builtin federation mappers when creating new LDAP Federation model. Testsuite passing --- .../keycloak/federation/ldap/LDAPConfig.java | 12 +- .../ldap/LDAPFederationProvider.java | 7 +- .../ldap/LDAPFederationProviderFactory.java | 77 ++++++++++++- .../ldap/LDAPIdentityStoreRegistry.java | 14 --- .../keycloak/federation/ldap/LDAPUtils.java | 108 ++---------------- .../org/keycloak/models/LDAPConstants.java | 1 + .../java/org/keycloak/models/RealmModel.java | 5 + ...erFederationEventAwareProviderFactory.java | 33 ++++++ ...erFederationProviderCreationEventImpl.java | 25 ++++ .../models/UserFederationProviderModel.java | 6 +- .../models/utils/KeycloakModelUtils.java | 32 ++++++ .../models/utils/ModelToRepresentation.java | 4 +- .../models/utils/RepresentationToModel.java | 23 +++- .../models/file/adapter/RealmAdapter.java | 17 ++- .../org/keycloak/models/jpa/RealmAdapter.java | 20 ++-- .../mongo/keycloak/adapters/RealmAdapter.java | 17 ++- .../FederationProvidersIntegrationTest.java | 4 +- .../federation/FederationTestUtils.java | 44 ++++++- .../federation/LDAPRoleMappingsTest.java | 47 +++----- .../federation/SyncProvidersTest.java | 5 +- .../keycloak/testsuite/model/ImportTest.java | 31 +++-- .../src/test/resources/model/testrealm.json | 16 ++- 22 files changed, 348 insertions(+), 200 deletions(-) create mode 100644 model/api/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java create mode 100644 model/api/src/main/java/org/keycloak/models/UserFederationProviderCreationEventImpl.java diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java index 65181e147b..acd2182484 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java @@ -11,6 +11,7 @@ import java.util.Set; import javax.naming.directory.SearchControls; import org.keycloak.models.LDAPConstants; +import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderModel; /** @@ -62,7 +63,7 @@ public class LDAPConfig { return dns.iterator().next(); } - public Collection getObjectClasses() { + public Collection getUserObjectClasses() { String objClassesCfg = config.get(LDAPConstants.USER_OBJECT_CLASSES); String objClassesStr = (objClassesCfg != null && objClassesCfg.length() > 0) ? objClassesCfg.trim() : "inetOrgPerson,organizationalPerson"; @@ -162,4 +163,13 @@ public class LDAPConfig { } return rdn; } + + public UserFederationProvider.EditMode getEditMode() { + String editModeString = config.get(LDAPConstants.EDIT_MODE); + if (editModeString == null) { + return UserFederationProvider.EditMode.READ_ONLY; + } else { + return UserFederationProvider.EditMode.valueOf(editModeString); + } + } } diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java index fcdc11c17d..21682823e3 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java @@ -59,12 +59,7 @@ public class LDAPFederationProvider implements UserFederationProvider { this.model = model; this.ldapIdentityStore = ldapIdentityStore; this.kerberosConfig = new LDAPProviderKerberosConfig(model); - String editModeString = model.getConfig().get(LDAPConstants.EDIT_MODE); - if (editModeString == null) { - editMode = EditMode.READ_ONLY; - } else { - editMode = EditMode.valueOf(editModeString); - } + this.editMode = ldapIdentityStore.getConfig().getEditMode(); supportedCredentialTypes.add(UserCredentialModel.PASSWORD); if (kerberosConfig.isAllowKerberosAuthentication()) { diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java index 54d76098eb..166c5bf27b 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java @@ -12,15 +12,22 @@ import org.keycloak.federation.ldap.idm.query.QueryParameter; import org.keycloak.federation.ldap.idm.query.internal.LDAPIdentityQuery; import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilder; import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore; +import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapper; +import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory; +import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapper; +import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapperFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionTask; import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; +import org.keycloak.models.UserFederationEventAwareProviderFactory; +import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderFactory; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationSyncResult; +import org.keycloak.models.UserModel; import org.keycloak.models.utils.KeycloakModelUtils; import java.util.Collections; @@ -33,7 +40,7 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class LDAPFederationProviderFactory implements UserFederationProviderFactory { +public class LDAPFederationProviderFactory extends UserFederationEventAwareProviderFactory { private static final Logger logger = Logger.getLogger(LDAPFederationProviderFactory.class); public static final String PROVIDER_NAME = "ldap"; @@ -55,11 +62,6 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact this.ldapStoreRegistry = new LDAPIdentityStoreRegistry(); } - @Override - public void postInit(KeycloakSessionFactory factory) { - - } - @Override public void close() { this.ldapStoreRegistry = null; @@ -75,6 +77,69 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact return Collections.emptySet(); } + + // Best effort to create appropriate mappers according to our LDAP config + @Override + protected void onProviderModelCreated(RealmModel realm, UserFederationProviderModel newProviderModel) { + LDAPConfig ldapConfig = new LDAPConfig(newProviderModel.getConfig()); + + boolean activeDirectory = ldapConfig.isActiveDirectory(); + UserFederationProvider.EditMode editMode = ldapConfig.getEditMode(); + String readOnly = String.valueOf(editMode==UserFederationProvider.EditMode.READ_ONLY || editMode== UserFederationProvider.EditMode.UNSYNCED); + String usernameLdapAttribute = ldapConfig.getUsernameLdapAttribute(); + + UserFederationMapperModel mapperModel; + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("usernameMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID, + UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME, + UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, usernameLdapAttribute, + UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); + realm.addUserFederationMapper(mapperModel); + + // For AD deployments with sAMAccountName is probably more common to map "cn" to full name of user + if (activeDirectory && usernameLdapAttribute.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) { + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("fullNameMapper", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.ID, + FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN, + UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); + realm.addUserFederationMapper(mapperModel); + } else { + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("firstNameMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID, + UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME, + UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN, + UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); + realm.addUserFederationMapper(mapperModel); + } + + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("lastNameMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID, + UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.LAST_NAME, + UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.SN, + UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); + realm.addUserFederationMapper(mapperModel); + + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("emailMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID, + UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.EMAIL, + UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.EMAIL, + UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); + realm.addUserFederationMapper(mapperModel); + + String createTimestampLdapAttrName = activeDirectory ? "whenCreated" : LDAPConstants.CREATE_TIMESTAMP; + String modifyTimestampLdapAttrName = activeDirectory ? "whenChanged" : LDAPConstants.MODIFY_TIMESTAMP; + + // map createTimeStamp as read-only + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("creationDateMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID, + UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.CREATE_TIMESTAMP, + UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, createTimestampLdapAttrName, + UserAttributeLDAPFederationMapper.READ_ONLY, "true"); + realm.addUserFederationMapper(mapperModel); + + // map modifyTimeStamp as read-only + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("modifyDateMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID, + UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.MODIFY_TIMESTAMP, + UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, modifyTimestampLdapAttrName, + UserAttributeLDAPFederationMapper.READ_ONLY, "true"); + realm.addUserFederationMapper(mapperModel); + } + + @Override public UserFederationSyncResult syncAllUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model) { logger.infof("Sync all users from LDAP to local store: realm: %s, federation provider: %s", realmId, model.getDisplayName()); diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java index 5cd42c03f0..97f347b80c 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java @@ -81,20 +81,6 @@ public class LDAPIdentityStoreRegistry { } } - // Parse array of strings like [ "inetOrgPerson", "organizationalPerson" ] from the string like: "inetOrgPerson, organizationalPerson" - /*private static String[] getUserObjectClasses(Map ldapConfig) { - String objClassesCfg = ldapConfig.get(LDAPConstants.USER_OBJECT_CLASSES); - String objClassesStr = (objClassesCfg != null && objClassesCfg.length() > 0) ? objClassesCfg.trim() : "inetOrgPerson, organizationalPerson"; - - String[] addObjectClasses = objClassesStr.split(","); - - // Trim them - String[] userObjectClasses = new String[addObjectClasses.length]; - for (int i=0 ; iMarek Posolda */ @@ -28,92 +27,24 @@ public class LDAPUtils { * @return newly created LDAPObject with all the attributes, uuid and DN properly set */ public static LDAPObject addUserToLDAP(LDAPFederationProvider ldapProvider, RealmModel realm, UserModel user) { - LDAPObject ldapObject = new LDAPObject(); + LDAPObject ldapUser = new LDAPObject(); LDAPIdentityStore ldapStore = ldapProvider.getLdapIdentityStore(); LDAPConfig ldapConfig = ldapStore.getConfig(); - ldapObject.setRdnAttributeName(ldapConfig.getRdnLdapAttribute()); - ldapObject.setObjectClasses(ldapConfig.getObjectClasses()); + ldapUser.setRdnAttributeName(ldapConfig.getRdnLdapAttribute()); + ldapUser.setObjectClasses(ldapConfig.getUserObjectClasses()); Set federationMappers = realm.getUserFederationMappers(); for (UserFederationMapperModel mapperModel : federationMappers) { LDAPFederationMapper ldapMapper = ldapProvider.getMapper(mapperModel); - ldapMapper.onRegisterUserToLDAP(mapperModel, ldapProvider, ldapObject, user, realm); + ldapMapper.onRegisterUserToLDAP(mapperModel, ldapProvider, ldapUser, user, realm); } - LDAPUtils.computeAndSetDn(ldapConfig, ldapObject); - ldapStore.add(ldapObject); - return ldapObject; - } - - /*public static LDAPUser updateUser(LDAPIdentityStore ldapIdentityStore, String username, String firstName, String lastName, String email) { - LDAPUser ldapUser = getUser(ldapIdentityStore, username); - ldapUser.setFirstName(firstName); - ldapUser.setLastName(lastName); - ldapUser.setEmail(email); - ldapIdentityStore.update(ldapUser); + LDAPUtils.computeAndSetDn(ldapConfig, ldapUser); + ldapStore.add(ldapUser); return ldapUser; } - public static void updatePassword(LDAPIdentityStore ldapIdentityStore, UserModel user, String password) { - LDAPUser ldapUser = convertUserForPasswordUpdate(user); - - ldapIdentityStore.updatePassword(ldapUser, password); - } - - public static void updatePassword(LDAPIdentityStore ldapIdentityStore, LDAPUser user, String password) { - ldapIdentityStore.updatePassword(user, password); - } - - public static boolean validatePassword(LDAPIdentityStore ldapIdentityStore, UserModel user, String password) { - LDAPUser ldapUser = convertUserForPasswordUpdate(user); - - return ldapIdentityStore.validatePassword(ldapUser, password); - } - - public static boolean validatePassword(LDAPIdentityStore ldapIdentityStore, LDAPUser user, String password) { - return ldapIdentityStore.validatePassword(user, password); - } - - public static LDAPUser getUser(LDAPIdentityStore ldapIdentityStore, String username) { - return ldapIdentityStore.getUser(username); - } - - // Put just username and entryDN as these are needed by LDAPIdentityStore for passwordUpdate - private static LDAPUser convertUserForPasswordUpdate(UserModel kcUser) { - LDAPUser ldapUser = new LDAPUser(kcUser.getUsername()); - String ldapEntryDN = kcUser.getAttribute(LDAPConstants.LDAP_ENTRY_DN); - if (ldapEntryDN != null) { - ldapUser.setEntryDN(ldapEntryDN); - } - return ldapUser; - } - - - public static LDAPUser getUserByEmail(LDAPIdentityStore ldapIdentityStore, String email) { - IdentityQueryBuilder queryBuilder = ldapIdentityStore.createQueryBuilder(); - LDAPIdentityQuery query = queryBuilder.createIdentityQuery(LDAPUser.class) - .where(queryBuilder.equal(LDAPUser.EMAIL, email)); - List users = query.getResultList(); - - if (users.isEmpty()) { - return null; - } else if (users.size() == 1) { - return users.get(0); - } else { - throw new ModelDuplicateException("Error - multiple users found with same email " + email); - } - } - - public static boolean removeUser(LDAPIdentityStore ldapIdentityStore, String username) { - LDAPUser ldapUser = getUser(ldapIdentityStore, username); - if (ldapUser == null) { - return false; - } - ldapIdentityStore.remove(ldapUser); - return true; - } */ - public static void removeAllUsers(LDAPFederationProvider ldapProvider, RealmModel realm) { LDAPIdentityStore ldapStore = ldapProvider.getLdapIdentityStore(); LDAPIdentityQuery ldapQuery = LDAPUtils.createQueryForUserSearch(ldapProvider, realm); @@ -129,7 +60,7 @@ public class LDAPUtils { LDAPConfig config = ldapProvider.getLdapIdentityStore().getConfig(); ldapQuery.setSearchScope(config.getSearchScope()); ldapQuery.addSearchDns(config.getUserDns()); - ldapQuery.addObjectClasses(config.getObjectClasses()); + ldapQuery.addObjectClasses(config.getUserObjectClasses()); Set mapperModels = realm.getUserFederationMappers(); ldapQuery.addMappers(mapperModels); @@ -137,31 +68,6 @@ public class LDAPUtils { return ldapQuery; } - /* - public static List getAllUsers(LDAPIdentityStore ldapIdentityStore) { - LDAPIdentityQuery userQuery = ldapIdentityStore.createQueryBuilder().createIdentityQuery(LDAPUser.class); - return userQuery.getResultList(); - } - - // Needed for ActiveDirectory updates - private static String getFullName(String username, String firstName, String lastName) { - String fullName; - if (firstName != null && lastName != null) { - fullName = firstName + " " + lastName; - } else if (firstName != null && firstName.trim().length() > 0) { - fullName = firstName; - } else { - fullName = lastName; - } - - // Fallback to loginName - if (fullName == null || fullName.trim().length() == 0) { - fullName = username; - } - - return fullName; - } */ - // ldapUser has filled attributes, but doesn't have filled dn public static void computeAndSetDn(LDAPConfig config, LDAPObject ldapObject) { String rdnLdapAttrName = config.getRdnLdapAttribute(); diff --git a/model/api/src/main/java/org/keycloak/models/LDAPConstants.java b/model/api/src/main/java/org/keycloak/models/LDAPConstants.java index 35323c0abd..ac15a5cb15 100644 --- a/model/api/src/main/java/org/keycloak/models/LDAPConstants.java +++ b/model/api/src/main/java/org/keycloak/models/LDAPConstants.java @@ -50,6 +50,7 @@ public class LDAPConstants { public static final String GIVENNAME = "givenname"; public static final String CN = "cn"; public static final String SN = "sn"; + public static final String SAM_ACCOUNT_NAME = "sAMAccountName"; public static final String EMAIL = "mail"; public static final String POSTAL_CODE = "postalCode"; public static final String MEMBER = "member"; diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java index bfcaefb172..508a84cdce 100755 --- a/model/api/src/main/java/org/keycloak/models/RealmModel.java +++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java @@ -24,6 +24,11 @@ public interface RealmModel extends RoleContainerModel { ClientModel getCreatedClient(); } + interface UserFederationProviderCreationEvent extends ProviderEvent { + UserFederationProviderModel getCreatedFederationProvider(); + RealmModel getRealm(); + } + String getId(); String getName(); diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java b/model/api/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java new file mode 100644 index 0000000000..866bf79881 --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java @@ -0,0 +1,33 @@ +package org.keycloak.models; + +import org.keycloak.provider.ProviderEvent; +import org.keycloak.provider.ProviderEventListener; + +/** + * Provides "onProviderModelCreated" callback invoked when UserFederationProviderModel for this factory implementation is created in realm + * + * @author Marek Posolda + */ +public abstract class UserFederationEventAwareProviderFactory implements UserFederationProviderFactory { + + @Override + public void postInit(KeycloakSessionFactory factory) { + factory.register(new ProviderEventListener() { + + @Override + public void onEvent(ProviderEvent event) { + if (event instanceof RealmModel.UserFederationProviderCreationEvent) { + RealmModel.UserFederationProviderCreationEvent fedCreationEvent = (RealmModel.UserFederationProviderCreationEvent)event; + UserFederationProviderModel providerModel = fedCreationEvent.getCreatedFederationProvider(); + + if (providerModel.getProviderName().equals(getId())) { + onProviderModelCreated(fedCreationEvent.getRealm(), providerModel); + } + } + } + + }); + } + + protected abstract void onProviderModelCreated(RealmModel realm, UserFederationProviderModel createdProviderModel); +} diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProviderCreationEventImpl.java b/model/api/src/main/java/org/keycloak/models/UserFederationProviderCreationEventImpl.java new file mode 100644 index 0000000000..995ddc858d --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/UserFederationProviderCreationEventImpl.java @@ -0,0 +1,25 @@ +package org.keycloak.models; + +/** + * @author Marek Posolda + */ +public class UserFederationProviderCreationEventImpl implements RealmModel.UserFederationProviderCreationEvent { + + private final UserFederationProviderModel createdFederationProvider; + private final RealmModel realm; + + public UserFederationProviderCreationEventImpl(RealmModel realm, UserFederationProviderModel createdFederationProvider) { + this.realm = realm; + this.createdFederationProvider = createdFederationProvider; + } + + @Override + public UserFederationProviderModel getCreatedFederationProvider() { + return createdFederationProvider; + } + + @Override + public RealmModel getRealm() { + return realm; + } +} diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java b/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java index 9c3bf1c159..494670d9ae 100755 --- a/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java +++ b/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java @@ -20,7 +20,7 @@ public class UserFederationProviderModel { private int changedSyncPeriod = -1; // In seconds. -1 means that periodic changed sync is disabled private int lastSync; // Date when last sync was done for this provider - public UserFederationProviderModel() {}; + public UserFederationProviderModel() {} public UserFederationProviderModel(String id, String providerName, Map config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) { this.id = id; @@ -39,6 +39,10 @@ public class UserFederationProviderModel { return id; } + public void setId(String id) { + this.id = id; + } + public String getProviderName() { return providerName; } diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index 382d9c0c8c..988d9ecfcc 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -10,6 +10,7 @@ import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; @@ -26,8 +27,10 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; @@ -273,6 +276,8 @@ public final class KeycloakModelUtils { return false; } + // USER FEDERATION RELATED STUFF + /** * Ensure that displayName of myProvider (if not null) is unique and there is no other provider with same displayName in the list. * @@ -296,6 +301,7 @@ public final class KeycloakModelUtils { } } + public static UserFederationProviderModel findUserFederationProviderByDisplayName(String displayName, RealmModel realm) { if (displayName == null) { return null; @@ -309,6 +315,7 @@ public final class KeycloakModelUtils { return null; } + public static UserFederationProviderModel findUserFederationProviderById(String fedProviderId, RealmModel realm) { for (UserFederationProviderModel fedProvider : realm.getUserFederationProviders()) { if (fedProviderId.equals(fedProvider.getId())) { @@ -317,4 +324,29 @@ public final class KeycloakModelUtils { } return null; } + + + public static UserFederationMapperModel createUserFederationMapperModel(String name, String federationProviderId, String mapperType, String... config) { + UserFederationMapperModel mapperModel = new UserFederationMapperModel(); + mapperModel.setName(name); + mapperModel.setFederationProviderId(federationProviderId); + mapperModel.setFederationMapperType(mapperType); + + Map configMap = new HashMap(); + String key = null; + for (String configEntry : config) { + if (key == null) { + key = configEntry; + } else { + configMap.put(key, configEntry); + key = null; + } + } + if (key != null) { + throw new IllegalStateException("Invalid count of arguments for config. Maybe mistake?"); + } + mapperModel.setConfig(configMap); + + return mapperModel; + } } diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 2d202667b2..67d5932a65 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -16,7 +16,7 @@ import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; - + import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.FederatedIdentityRepresentation; @@ -307,7 +307,7 @@ public class ModelToRepresentation { config.putAll(model.getConfig()); rep.setConfig(config); - UserFederationProviderModel fedProvider = KeycloakModelUtils.findUserFederationProviderById(model.getId(), realm); + UserFederationProviderModel fedProvider = KeycloakModelUtils.findUserFederationProviderById(model.getFederationProviderId(), realm); if (fedProvider == null) { throw new ModelException("Couldn't find federation provider with ID " + model.getId()); } diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index f59f0b5629..715b14a9c3 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -49,6 +49,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; public class RepresentationToModel { @@ -239,11 +240,31 @@ public class RepresentationToModel { newRealm.setBrowserSecurityHeaders(BrowserSecurityHeaders.defaultHeaders); } + List providerModels = null; if (rep.getUserFederationProviders() != null) { - List providerModels = convertFederationProviders(rep.getUserFederationProviders()); + providerModels = convertFederationProviders(rep.getUserFederationProviders()); newRealm.setUserFederationProviders(providerModels); } if (rep.getUserFederationMappers() != null) { + + // Remove builtin mappers for federation providers, which have some mappers already provided in JSON (likely due to previous export) + if (rep.getUserFederationProviders() != null) { + Set providerNames = new TreeSet(); + for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) { + providerNames.add(representation.getFederationProviderDisplayName()); + } + for (String providerName : providerNames) { + for (UserFederationProviderModel providerModel : providerModels) { + if (providerName.equals(providerModel.getDisplayName())) { + Set toDelete = newRealm.getUserFederationMappersByFederationProvider(providerModel.getId()); + for (UserFederationMapperModel mapperModel : toDelete) { + newRealm.removeUserFederationMapper(mapperModel); + } + } + } + } + } + for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) { newRealm.addUserFederationMapper(toModel(newRealm, representation)); } diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java index 4092ffab4c..30b272d2fd 100755 --- a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java +++ b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java @@ -28,6 +28,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserFederationMapperModel; +import org.keycloak.models.UserFederationProviderCreationEventImpl; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; import org.keycloak.models.entities.ClientEntity; @@ -829,7 +830,9 @@ public class RealmAdapter implements RealmModel { entity.setLastSync(lastSync); realm.getUserFederationProviders().add(entity); - return new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync); + UserFederationProviderModel providerModel = new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync); + session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, providerModel)); + return providerModel; } @Override @@ -907,8 +910,13 @@ public class RealmAdapter implements RealmModel { List entities = new LinkedList(); for (UserFederationProviderModel model : providers) { UserFederationProviderEntity entity = new UserFederationProviderEntity(); - if (model.getId() != null) entity.setId(model.getId()); - else entity.setId(KeycloakModelUtils.generateId()); + if (model.getId() != null) { + entity.setId(model.getId()); + } else { + String id = KeycloakModelUtils.generateId(); + entity.setId(id); + model.setId(id); + } entity.setProviderName(model.getProviderName()); entity.setConfig(model.getConfig()); entity.setPriority(model.getPriority()); @@ -921,6 +929,7 @@ public class RealmAdapter implements RealmModel { entity.setChangedSyncPeriod(model.getChangedSyncPeriod()); entity.setLastSync(model.getLastSync()); entities.add(entity); + session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, model)); } realm.setUserFederationProviders(entities); @@ -1205,7 +1214,7 @@ public class RealmAdapter implements RealmModel { @Override public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel model) { if (getUserFederationMapperByName(model.getFederationProviderId(), model.getName()) != null) { - throw new ModelDuplicateException("User federation mapper must be unique per federation provider"); + throw new ModelDuplicateException("User federation mapper must be unique per federation provider. There is already: " + model.getName()); } String id = KeycloakModelUtils.generateId(); UserFederationMapperEntity entity = new UserFederationMapperEntity(); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index 990b087b4b..5c1d7e3624 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -6,13 +6,12 @@ import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.ModelException; import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; +import org.keycloak.models.UserFederationProviderCreationEventImpl; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.jpa.entities.ClientEntity; import org.keycloak.models.jpa.entities.IdentityProviderEntity; @@ -783,7 +782,9 @@ public class RealmAdapter implements RealmModel { em.persist(entity); realm.getUserFederationProviders().add(entity); em.flush(); - return new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync); + UserFederationProviderModel providerModel = new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync); + session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, providerModel)); + return providerModel; } @Override @@ -879,8 +880,13 @@ public class RealmAdapter implements RealmModel { for (UserFederationProviderModel model : add) { UserFederationProviderEntity entity = new UserFederationProviderEntity(); - if (model.getId() != null) entity.setId(model.getId()); - else entity.setId(KeycloakModelUtils.generateId()); + if (model.getId() != null) { + entity.setId(model.getId()); + } else { + String id = KeycloakModelUtils.generateId(); + entity.setId(id); + model.setId(id); + } entity.setConfig(model.getConfig()); entity.setPriority(model.getPriority()); entity.setProviderName(model.getProviderName()); @@ -895,7 +901,7 @@ public class RealmAdapter implements RealmModel { entity.setLastSync(model.getLastSync()); em.persist(entity); realm.getUserFederationProviders().add(entity); - + session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, model)); } } @@ -1385,7 +1391,7 @@ public class RealmAdapter implements RealmModel { @Override public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel model) { if (getUserFederationMapperByName(model.getFederationProviderId(), model.getName()) != null) { - throw new ModelDuplicateException("User federation mapper must be unique per federation provider"); + throw new ModelDuplicateException("User federation mapper must be unique per federation provider. There is already: " + model.getName()); } String id = KeycloakModelUtils.generateId(); UserFederationMapperEntity entity = new UserFederationMapperEntity(); diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java index a327a8d050..a36f83e645 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java @@ -16,6 +16,7 @@ import org.keycloak.models.RealmProvider; import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserFederationMapperModel; +import org.keycloak.models.UserFederationProviderCreationEventImpl; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.entities.IdentityProviderEntity; import org.keycloak.models.entities.IdentityProviderMapperEntity; @@ -852,7 +853,9 @@ public class RealmAdapter extends AbstractMongoAdapter impleme realm.getUserFederationProviders().add(entity); updateRealm(); - return new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync); + UserFederationProviderModel providerModel = new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync); + session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, providerModel)); + return providerModel; } @Override @@ -932,8 +935,13 @@ public class RealmAdapter extends AbstractMongoAdapter impleme List entities = new LinkedList(); for (UserFederationProviderModel model : providers) { UserFederationProviderEntity entity = new UserFederationProviderEntity(); - if (model.getId() != null) entity.setId(model.getId()); - else entity.setId(KeycloakModelUtils.generateId()); + if (model.getId() != null) { + entity.setId(model.getId()); + } else { + String id = KeycloakModelUtils.generateId(); + entity.setId(id); + model.setId(id); + } entity.setProviderName(model.getProviderName()); entity.setConfig(model.getConfig()); entity.setPriority(model.getPriority()); @@ -946,6 +954,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme entity.setChangedSyncPeriod(model.getChangedSyncPeriod()); entity.setLastSync(model.getLastSync()); entities.add(entity); + session.getKeycloakSessionFactory().publish(new UserFederationProviderCreationEventImpl(this, model)); } realm.setUserFederationProviders(entities); @@ -1236,7 +1245,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme @Override public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel model) { if (getUserFederationMapperByName(model.getFederationProviderId(), model.getName()) != null) { - throw new ModelDuplicateException("User federation mapper must be unique per federation provider"); + throw new ModelDuplicateException("User federation mapper must be unique per federation provider. There is already: " + model.getName()); } String id = KeycloakModelUtils.generateId(); UserFederationMapperEntity entity = new UserFederationMapperEntity(); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java index 966260a559..aa61ee6974 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java @@ -23,8 +23,6 @@ import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; -import org.keycloak.models.utils.UserModelDelegate; -import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.pages.AccountPasswordPage; @@ -61,6 +59,7 @@ public class FederationProvidersIntegrationTest { ldapConfig.put(LDAPConstants.EDIT_MODE, UserFederationProvider.EditMode.WRITABLE.toString()); ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap", -1, -1, 0); + FederationTestUtils.addZipCodeLDAPMapper(appRealm, ldapModel); // Delete all LDAP users and add some new for testing LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel); @@ -150,6 +149,7 @@ public class FederationProvidersIntegrationTest { RealmModel appRealm = manager.getRealm("test"); ldapModel = appRealm.addUserFederationProvider(ldapModel.getProviderName(), ldapModel.getConfig(), ldapModel.getPriority(), ldapModel.getDisplayName(), -1, -1, 0); + FederationTestUtils.addZipCodeLDAPMapper(appRealm, ldapModel); } finally { keycloakRule.stopSession(session, true); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java index f5dfc9a312..ddda4ff856 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java @@ -5,13 +5,20 @@ import org.keycloak.federation.ldap.LDAPFederationProvider; import org.keycloak.federation.ldap.LDAPFederationProviderFactory; import org.keycloak.federation.ldap.LDAPUtils; import org.keycloak.federation.ldap.idm.model.LDAPObject; +import org.keycloak.federation.ldap.mappers.RoleLDAPFederationMapper; +import org.keycloak.federation.ldap.mappers.RoleLDAPFederationMapperFactory; +import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapper; +import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapperFactory; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserProvider; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.UserModelDelegate; import org.keycloak.representations.idm.CredentialRepresentation; @@ -59,7 +66,7 @@ class FederationTestUtils { @Override public String getAttribute(String name) { - if (name == "postal_code") { + if ("postal_code".equals(name)) { return postalCode; } else { return null; @@ -82,4 +89,39 @@ class FederationTestUtils { Assert.assertEquals(expectedEmail, user.getEmail()); Assert.assertEquals(expectedPostalCode, user.getAttribute("postal_code")); } + + public static void addZipCodeLDAPMapper(RealmModel realm, UserFederationProviderModel providerModel) { + UserFederationMapperModel mapperModel = KeycloakModelUtils.createUserFederationMapperModel("zipCodeMapper", providerModel.getId(), UserAttributeLDAPFederationMapperFactory.ID, + UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "postal_code", + UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.POSTAL_CODE, + UserAttributeLDAPFederationMapper.READ_ONLY, "false"); + realm.addUserFederationMapper(mapperModel); + } + + public static void addOrUpdateRoleLDAPMappers(RealmModel realm, UserFederationProviderModel providerModel, RoleLDAPFederationMapper.Mode mode) { + UserFederationMapperModel mapperModel = realm.getUserFederationMapperByName(providerModel.getId(), "realmRolesMapper"); + if (mapperModel != null) { + mapperModel.getConfig().put(RoleLDAPFederationMapper.MODE, mode.toString()); + realm.updateUserFederationMapper(mapperModel); + } else { + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("realmRolesMapper", providerModel.getId(), RoleLDAPFederationMapperFactory.ID, + RoleLDAPFederationMapper.ROLES_DN, "ou=RealmRoles,dc=keycloak,dc=org", + RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING, "true", + RoleLDAPFederationMapper.MODE, mode.toString()); + realm.addUserFederationMapper(mapperModel); + } + + mapperModel = realm.getUserFederationMapperByName(providerModel.getId(), "financeRolesMapper"); + if (mapperModel != null) { + mapperModel.getConfig().put(RoleLDAPFederationMapper.MODE, mode.toString()); + realm.updateUserFederationMapper(mapperModel); + } else { + mapperModel = KeycloakModelUtils.createUserFederationMapperModel("financeRolesMapper", providerModel.getId(), RoleLDAPFederationMapperFactory.ID, + RoleLDAPFederationMapper.ROLES_DN, "ou=FinanceRoles,dc=keycloak,dc=org", + RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING, "false", + RoleLDAPFederationMapper.CLIENT_ID, "finance", + RoleLDAPFederationMapper.MODE, mode.toString()); + realm.addUserFederationMapper(mapperModel); + } + } } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java index eff3572bc1..a389fe525d 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java @@ -1,6 +1,5 @@ package org.keycloak.testsuite.federation; -import java.util.List; import java.util.Map; import java.util.Set; @@ -33,7 +32,6 @@ import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; -import org.keycloak.models.cache.RealmAdapter; import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.pages.AccountPasswordPage; @@ -96,7 +94,9 @@ public class LDAPRoleMappingsTest { @Override public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { RoleLDAPFederationMapper roleMapper = new RoleLDAPFederationMapper(); - UserFederationMapperModel roleMapperModel = findRoleMapperModel(appRealm); + + FederationTestUtils.addOrUpdateRoleLDAPMappers(appRealm, ldapModel, RoleLDAPFederationMapper.Mode.LDAP_ONLY); + UserFederationMapperModel roleMapperModel = appRealm.getUserFederationMapperByName(ldapModel.getId(), "realmRolesMapper"); LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, ldapModel); LDAPObject ldapRole = roleMapper.loadLDAPRoleByName(roleMapperModel, ldapProvider, "realmRole3"); @@ -129,26 +129,17 @@ public class LDAPRoleMappingsTest { @WebResource protected AppPage appPage; - @WebResource - protected RegisterPage registerPage; - @WebResource protected LoginPage loginPage; - @WebResource - protected AccountUpdateProfilePage profilePage; - - @WebResource - protected AccountPasswordPage changePasswordPage; - @Test public void test01_ldapOnlyRoleMappings() { - // TODO: Remove me!!! - //RealmAdapter.LDAP_MODE = "LDAP_ONLY"; - KeycloakSession session = keycloakRule.startSession(); try { RealmModel appRealm = session.realms().getRealmByName("test"); + + FederationTestUtils.addOrUpdateRoleLDAPMappers(appRealm, ldapModel, RoleLDAPFederationMapper.Mode.LDAP_ONLY); + UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm); UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm); @@ -230,12 +221,12 @@ public class LDAPRoleMappingsTest { @Test public void test02_readOnlyRoleMappings() { - // TODO: Remove me!!! - //RealmAdapter.LDAP_MODE = "READ_ONLY"; - KeycloakSession session = keycloakRule.startSession(); try { RealmModel appRealm = session.realms().getRealmByName("test"); + + FederationTestUtils.addOrUpdateRoleLDAPMappers(appRealm, ldapModel, RoleLDAPFederationMapper.Mode.READ_ONLY); + UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm); RoleModel realmRole1 = appRealm.getRole("realmRole1"); @@ -247,7 +238,7 @@ public class LDAPRoleMappingsTest { // Add some role mappings directly into LDAP RoleLDAPFederationMapper roleMapper = new RoleLDAPFederationMapper(); - UserFederationMapperModel roleMapperModel = findRoleMapperModel(appRealm); + UserFederationMapperModel roleMapperModel = appRealm.getUserFederationMapperByName(ldapModel.getId(), "realmRolesMapper"); LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, ldapModel); LDAPObject maryLdap = ldapProvider.loadLDAPUserByUsername(appRealm, "marykeycloak"); roleMapper.addRoleMappingInLDAP(roleMapperModel, "realmRole1", ldapProvider, maryLdap); @@ -292,16 +283,15 @@ public class LDAPRoleMappingsTest { @Test public void test03_importRoleMappings() { - // TODO: Remove me!!! - //RealmAdapter.LDAP_MODE = "IMPORT"; - KeycloakSession session = keycloakRule.startSession(); try { RealmModel appRealm = session.realms().getRealmByName("test"); + FederationTestUtils.addOrUpdateRoleLDAPMappers(appRealm, ldapModel, RoleLDAPFederationMapper.Mode.IMPORT); + // Add some role mappings directly in LDAP RoleLDAPFederationMapper roleMapper = new RoleLDAPFederationMapper(); - UserFederationMapperModel roleMapperModel = findRoleMapperModel(appRealm); + UserFederationMapperModel roleMapperModel = appRealm.getUserFederationMapperByName(ldapModel.getId(), "realmRolesMapper"); LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, ldapModel); LDAPObject robLdap = ldapProvider.loadLDAPUserByUsername(appRealm, "robkeycloak"); roleMapper.addRoleMappingInLDAP(roleMapperModel, "realmRole1", ldapProvider, robLdap); @@ -345,17 +335,6 @@ public class LDAPRoleMappingsTest { } } - private static UserFederationMapperModel findRoleMapperModel(RealmModel appRealm) { - Set fedMappers = appRealm.getUserFederationMappers(); - for (UserFederationMapperModel mapper : fedMappers) { - if ("realmRoleMapper".equals(mapper.getName())) { - return mapper; - } - } - - throw new IllegalStateException("Mapper 'realmRoleMapper' not found"); - } - private void deleteRoleMappingsInLDAP(UserFederationMapperModel roleMapperModel, RoleLDAPFederationMapper roleMapper, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, String roleName) { LDAPIdentityQuery ldapQuery = roleMapper.createRoleQuery(roleMapperModel, ldapProvider); LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder(); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java index 71a37b77cf..40ae50c8d0 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java @@ -19,7 +19,6 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationSyncResult; -import org.keycloak.models.UserModel; import org.keycloak.models.UserProvider; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.UsersSyncManager; @@ -52,11 +51,13 @@ public class SyncProvidersTest { Map ldapConfig = ldapRule.getConfig(); ldapConfig.put(LDAPConstants.SYNC_REGISTRATIONS, "false"); - ldapConfig.put(LDAPConstants.EDIT_MODE, UserFederationProvider.EditMode.UNSYNCED.toString()); + ldapConfig.put(LDAPConstants.EDIT_MODE, UserFederationProvider.EditMode.WRITABLE.toString()); ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap", -1, -1, 0); + FederationTestUtils.addZipCodeLDAPMapper(appRealm, ldapModel); + // Delete all LDAP users and add 5 new users for testing LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel); LDAPUtils.removeAllUsers(ldapFedProvider, appRealm); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java index 492aa1d2c0..4d20bc8eec 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java @@ -12,6 +12,7 @@ import org.keycloak.models.Constants; import org.keycloak.models.FederatedIdentityModel; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.LDAPConstants; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RequiredCredentialModel; @@ -216,22 +217,32 @@ public class ImportTest extends AbstractModelTest { // Test federation providers List fedProviders = realm.getUserFederationProviders(); - Assert.assertTrue(fedProviders.size() == 1); - UserFederationProviderModel ldap = fedProviders.get(0); - Assert.assertEquals("MyLDAPProvider", ldap.getDisplayName()); - Assert.assertEquals("dummy", ldap.getProviderName()); - Assert.assertEquals(1, ldap.getPriority()); - Assert.assertEquals("ldap://foo", ldap.getConfig().get("important.config")); + Assert.assertTrue(fedProviders.size() == 2); + UserFederationProviderModel ldap1 = fedProviders.get(0); + Assert.assertEquals("MyLDAPProvider1", ldap1.getDisplayName()); + Assert.assertEquals("ldap", ldap1.getProviderName()); + Assert.assertEquals(1, ldap1.getPriority()); + Assert.assertEquals("ldap://foo", ldap1.getConfig().get(LDAPConstants.CONNECTION_URL)); + + UserFederationProviderModel ldap2 = fedProviders.get(1); + Assert.assertEquals("MyLDAPProvider2", ldap2.getDisplayName()); + Assert.assertEquals("ldap://bar", ldap2.getConfig().get(LDAPConstants.CONNECTION_URL)); // Test federation mappers - Set fedMappers = realm.getUserFederationMappers(); - Assert.assertTrue(fedMappers.size() == 1); - UserFederationMapperModel fullNameMapper = fedMappers.iterator().next(); + Set fedMappers1 = realm.getUserFederationMappersByFederationProvider(ldap1.getId()); + Assert.assertTrue(fedMappers1.size() == 1); + UserFederationMapperModel fullNameMapper = fedMappers1.iterator().next(); Assert.assertEquals("FullNameMapper", fullNameMapper.getName()); Assert.assertEquals(FullNameLDAPFederationMapperFactory.ID, fullNameMapper.getFederationMapperType()); - Assert.assertEquals(ldap.getId(), fullNameMapper.getFederationProviderId()); + Assert.assertEquals(ldap1.getId(), fullNameMapper.getFederationProviderId()); Assert.assertEquals("cn", fullNameMapper.getConfig().get(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE)); + // All builtin LDAP mappers should be here + Set fedMappers2 = realm.getUserFederationMappersByFederationProvider(ldap2.getId()); + Assert.assertTrue(fedMappers2.size() > 3); + Set allMappers = realm.getUserFederationMappers(); + Assert.assertEquals(allMappers.size(), fedMappers1.size() + fedMappers2.size()); + // Assert that federation link wasn't created during import UserFederationProviderFactory factory = (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, "dummy"); Assert.assertNull(factory.getInstance(session, null).getUserByUsername(realm, "wburke")); diff --git a/testsuite/integration/src/test/resources/model/testrealm.json b/testsuite/integration/src/test/resources/model/testrealm.json index 43458c93c5..0e41313879 100755 --- a/testsuite/integration/src/test/resources/model/testrealm.json +++ b/testsuite/integration/src/test/resources/model/testrealm.json @@ -25,18 +25,26 @@ ], "userFederationProviders": [ { - "displayName": "MyLDAPProvider", - "providerName": "dummy", + "displayName": "MyLDAPProvider1", + "providerName": "ldap", "priority": 1, "config": { - "important.config": "ldap://foo" + "connectionUrl": "ldap://foo" + } + }, + { + "displayName": "MyLDAPProvider2", + "providerName": "ldap", + "priority": 2, + "config": { + "connectionUrl": "ldap://bar" } } ], "userFederationMappers": [ { "name": "FullNameMapper", - "federationProviderDisplayName": "MyLDAPProvider", + "federationProviderDisplayName": "MyLDAPProvider1", "federationMapperType": "full-name-ldap-mapper", "config": { "ldap.full.name.attribute": "cn"