keycloak-aplcache

KEYCLOAK-886 Added builtin federation mappers when creating

5/22/2015 10:04:28 AM

Changes

Details

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 65181e1..acd2182 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<String> getObjectClasses() {
+    public Collection<String> 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 fcdc11c..2168282 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 54d7609..166c5bf 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @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";
 
@@ -56,11 +63,6 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
     }
 
     @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 5cd42c0..97f347b 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<String,String> 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 ; i<addObjectClasses.length ; i++) {
-            userObjectClasses[i] = addObjectClasses[i].trim();
-        }
-        return userObjectClasses;
-    }   */
 
     private class LDAPIdentityStoreContext {
 
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
index 1e9a9e3..37e110c 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
@@ -15,7 +15,6 @@ import org.keycloak.models.UserModel;
 
 /**
  * Allow to directly call some operations against LDAPIdentityStore.
- * TODO: Is this class still needed?
  *
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
@@ -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<UserFederationMapperModel> 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);
-        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);
-        }
+        LDAPUtils.computeAndSetDn(ldapConfig, ldapUser);
+        ldapStore.add(ldapUser);
         return ldapUser;
     }
 
-
-    public static LDAPUser getUserByEmail(LDAPIdentityStore ldapIdentityStore, String email) {
-        IdentityQueryBuilder queryBuilder = ldapIdentityStore.createQueryBuilder();
-        LDAPIdentityQuery<LDAPUser> query = queryBuilder.createIdentityQuery(LDAPUser.class)
-                .where(queryBuilder.equal(LDAPUser.EMAIL, email));
-        List<LDAPUser> 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<UserFederationMapperModel> mapperModels = realm.getUserFederationMappers();
         ldapQuery.addMappers(mapperModels);
@@ -137,31 +68,6 @@ public class LDAPUtils {
         return ldapQuery;
     }
 
-    /*
-    public static List<LDAPUser> getAllUsers(LDAPIdentityStore ldapIdentityStore) {
-        LDAPIdentityQuery<LDAPUser> 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 35323c0..ac15a5c 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 bfcaefb..508a84c 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 0000000..866bf79
--- /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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+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 0000000..995ddc8
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationProviderCreationEventImpl.java
@@ -0,0 +1,25 @@
+package org.keycloak.models;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+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 9c3bf1c..494670d 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<String, String> 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 382d9c0..988d9ec 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<String, String> configMap = new HashMap<String, String>();
+        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 2d20266..67d5932 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 f59f0b5..715b14a 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<UserFederationProviderModel> providerModels = null;
         if (rep.getUserFederationProviders() != null) {
-            List<UserFederationProviderModel> 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<String> providerNames = new TreeSet<String>();
+                for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) {
+                    providerNames.add(representation.getFederationProviderDisplayName());
+                }
+                for (String providerName : providerNames) {
+                    for (UserFederationProviderModel providerModel : providerModels) {
+                        if (providerName.equals(providerModel.getDisplayName())) {
+                            Set<UserFederationMapperModel> 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 4092ffa..30b272d 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<UserFederationProviderEntity> entities = new LinkedList<UserFederationProviderEntity>();
         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 990b087..5c1d7e3 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 a327a8d..a36f83e 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<MongoRealmEntity> 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<MongoRealmEntity> impleme
         List<UserFederationProviderEntity> entities = new LinkedList<UserFederationProviderEntity>();
         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<MongoRealmEntity> 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<MongoRealmEntity> 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 966260a..aa61ee6 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 f5dfc9a..ddda4ff 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 eff3572..a389fe5 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");
@@ -130,25 +130,16 @@ public class LDAPRoleMappingsTest {
     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<UserFederationMapperModel> 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 71a37b7..40ae50c 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<String,String> 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 492aa1d..4d20bc8 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<UserFederationProviderModel> 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<UserFederationMapperModel> fedMappers = realm.getUserFederationMappers();
-        Assert.assertTrue(fedMappers.size() == 1);
-        UserFederationMapperModel fullNameMapper = fedMappers.iterator().next();
+        Set<UserFederationMapperModel> 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<UserFederationMapperModel> fedMappers2 = realm.getUserFederationMappersByFederationProvider(ldap2.getId());
+        Assert.assertTrue(fedMappers2.size() > 3);
+        Set<UserFederationMapperModel> 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 43458c9..0e41313 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"