keycloak-aplcache

ldap jpa migration

11/10/2016 7:52:18 PM

Changes

Details

diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index 526a0cf..3dcb6e5 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -69,6 +69,10 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-ldap-storage</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-kerberos-federation</artifactId>
         </dependency>
         <!-- saml -->
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml
index 43a1d64..58977da 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml
@@ -21,7 +21,7 @@
     </properties>
 
     <resources>
-        <artifact name="${org.keycloak:keycloak-ldap-federation}"/>
+        <artifact name="${org.keycloak:keycloak-ldap-storage}"/>
     </resources>
 
     <dependencies>
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
index 8252f6b..069b34a 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
@@ -1341,6 +1341,13 @@ public class RealmAdapter implements CachedRealmModel {
         return updated.addComponentModel(model);
     }
 
+    @Override
+    public ComponentModel importComponentModel(ComponentModel model) {
+        getDelegateForUpdate();
+        evictUsers(model);
+        return updated.importComponentModel(model);
+    }
+
     public void evictUsers(ComponentModel model) {
         String parentId = model.getParentId();
         evictUsers(parentId);
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AbstractUserFedToComponent.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AbstractUserFedToComponent.java
new file mode 100644
index 0000000..e86788b
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AbstractUserFedToComponent.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.connections.jpa.updater.liquibase.custom;
+
+import liquibase.exception.CustomChangeException;
+import liquibase.statement.core.DeleteStatement;
+import liquibase.statement.core.InsertStatement;
+import liquibase.structure.core.Table;
+import org.jboss.logging.Logger;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.storage.UserStorageProvider;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public abstract class AbstractUserFedToComponent extends CustomKeycloakTask {
+    private final Logger logger = Logger.getLogger(getClass());
+    protected void convertFedProviderToComponent(String providerId, String newMapperType) throws CustomChangeException {
+        try {
+            PreparedStatement statement = jdbcConnection.prepareStatement("select ID, REALM_ID, PRIORITY, DISPLAY_NAME, FULL_SYNC_PERIOD, CHANGED_SYNC_PERIOD, LAST_SYNC from " + getTableName("USER_FEDERATION_PROVIDER") + " WHERE PROVIDER_NAME='" + providerId + "'");
+
+            try {
+                ResultSet resultSet = statement.executeQuery();
+                try {
+                    while (resultSet.next()) {
+                        int index = 1;
+                        String id = resultSet.getString(index++);
+                        String realmId = resultSet.getString(index++);
+                        int priority = resultSet.getInt(index++);
+                        String displayName = resultSet.getString(index++);
+                        int fullSyncPeriod = resultSet.getInt(index++);
+                        int changedSyncPeriod = resultSet.getInt(index++);
+                        int lastSync = resultSet.getInt(index++);
+
+
+                        InsertStatement insertComponent = new InsertStatement(null, null, database.correctObjectName("COMPONENT", Table.class))
+                                .addColumnValue("ID", id)
+                                .addColumnValue("REALM_ID", realmId)
+                                .addColumnValue("PARENT_ID", realmId)
+                                .addColumnValue("NAME", displayName)
+                                .addColumnValue("PROVIDER_ID", LDAPConstants.LDAP_PROVIDER)
+                                .addColumnValue("PROVIDER_TYPE", UserStorageProvider.class.getName());
+
+                        statements.add(insertComponent);
+
+                        statements.add(componentConfigStatement(id, "priority", Integer.toString(priority)));
+                        statements.add(componentConfigStatement(id, "fullSyncPeriod", Integer.toString(fullSyncPeriod)));
+                        statements.add(componentConfigStatement(id, "changedSyncPeriod", Integer.toString(changedSyncPeriod)));
+                        statements.add(componentConfigStatement(id, "lastSync", Integer.toString(lastSync)));
+                        PreparedStatement configStatement = jdbcConnection.prepareStatement("select name, VALUE from " + getTableName("USER_FEDERATION_CONFIG") + " WHERE USER_FEDERATION_PROVIDER_ID=?");
+                        configStatement.setString(1, id);
+                        try {
+                            ResultSet configSet = configStatement.executeQuery();
+                            try {
+                                while (configSet.next()) {
+                                    String name = configSet.getString(1);
+                                    String value = configSet.getString(2);
+                                    //logger.info("adding component config: " + name + ": " + value);
+                                    statements.add(componentConfigStatement(id, name, value));
+                                }
+                            } finally {
+                                configSet.close();
+                            }
+                        } finally {
+                            configStatement.close();
+                        }
+
+                        if (newMapperType != null) {
+                            convertFedMapperToComponent(realmId, id, newMapperType);
+                        }
+
+                        DeleteStatement configDelete = new DeleteStatement(null, null, database.correctObjectName("USER_FEDERATION_CONFIG", Table.class));
+                        configDelete.setWhere("USER_FEDERATION_PROVIDER_ID='" + id + "'");
+                        statements.add(configDelete);
+                        DeleteStatement deleteStatement = new DeleteStatement(null, null, database.correctObjectName("USER_FEDERATION_PROVIDER", Table.class));
+                        deleteStatement.setWhere("ID='" + id + "'");
+                        statements.add(deleteStatement);
+
+                    }
+                } finally {
+                    resultSet.close();
+                }
+            } finally {
+                statement.close();
+            }
+
+            confirmationMessage.append("Updated " + statements.size() + " records in USER_FEDERATION_PROVIDER table for " + providerId + " conversion to component model");
+        } catch (Exception e) {
+            throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
+        }
+    }
+
+    protected InsertStatement componentConfigStatement(String componentId, String name, String value) {
+        return new InsertStatement(null, null, database.correctObjectName("COMPONENT_CONFIG", Table.class))
+                .addColumnValue("ID", KeycloakModelUtils.generateId())
+                .addColumnValue("COMPONENT_ID", componentId)
+                .addColumnValue("NAME", name)
+                .addColumnValue("VALUE", value);
+    }
+
+    protected void convertFedMapperToComponent(String realmId, String parentId, String newMapperType) throws CustomChangeException {
+        try {
+            PreparedStatement statement = jdbcConnection.prepareStatement("select ID, NAME, FEDERATION_MAPPER_TYPE from " + getTableName("USER_FEDERATION_MAPPER") + " WHERE FEDERATION_PROVIDER_ID='" + parentId + "'");
+
+            try {
+                ResultSet resultSet = statement.executeQuery();
+                try {
+                    while (resultSet.next()) {
+                        String id = resultSet.getString(1);
+                        String mapperName = resultSet.getString(2);
+                        String fedMapperType = resultSet.getString(3);
+
+                        InsertStatement insertComponent = new InsertStatement(null, null, database.correctObjectName("COMPONENT", Table.class))
+                                .addColumnValue("ID", id)
+                                .addColumnValue("REALM_ID", realmId)
+                                .addColumnValue("PARENT_ID", parentId)
+                                .addColumnValue("NAME", mapperName)
+                                .addColumnValue("PROVIDER_ID", fedMapperType)
+                                .addColumnValue("PROVIDER_TYPE", newMapperType);
+
+                        statements.add(insertComponent);
+
+
+
+                        PreparedStatement configStatement = jdbcConnection.prepareStatement("select name, VALUE from " + getTableName("USER_FEDERATION_MAPPER_CONFIG") + " WHERE USER_FEDERATION_MAPPER_ID=?");
+                        configStatement.setString(1, id);
+                        try {
+                            ResultSet configSet = configStatement.executeQuery();
+                            try {
+                                while (configSet.next()) {
+                                    String name = configSet.getString(1);
+                                    String value = configSet.getString(2);
+                                    statements.add(componentConfigStatement(id, name, value));
+                                }
+                            } finally {
+                                configSet.close();
+                            }
+                        } finally {
+                            configStatement.close();
+                        }
+                        DeleteStatement configDelete = new DeleteStatement(null, null, database.correctObjectName("USER_FEDERATION_MAPPER_CONFIG", Table.class));
+                        configDelete.setWhere("USER_FEDERATION_MAPPER_ID='" + id + "'");
+                        statements.add(configDelete);
+                        DeleteStatement deleteStatement = new DeleteStatement(null, null, database.correctObjectName("USER_FEDERATION_MAPPER", Table.class));
+                        deleteStatement.setWhere("ID='" + id + "'");
+                        statements.add(deleteStatement);
+
+
+                    }
+                } finally {
+                    resultSet.close();
+                }
+            } finally {
+                statement.close();
+            }
+
+            confirmationMessage.append("Updated " + statements.size() + " records in USER_FEDERATION_MAPPER table for " + parentId + " conversion to component model");
+        } catch (Exception e) {
+            throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
+        }
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java
new file mode 100644
index 0000000..cf224b2
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.connections.jpa.updater.liquibase.custom;
+
+import liquibase.exception.CustomChangeException;
+import liquibase.statement.core.InsertStatement;
+import liquibase.structure.core.Table;
+import org.keycloak.keys.KeyProvider;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.storage.UserStorageProvider;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class PortLdapUserFedToComponentModel extends AbstractUserFedToComponent {
+
+    @Override
+    protected void generateStatementsImpl() throws CustomChangeException {
+        String providerId = LDAPConstants.LDAP_PROVIDER;
+        convertFedProviderToComponent(LDAPConstants.LDAP_PROVIDER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper");
+    }
+
+    @Override
+    protected String getTaskId() {
+        return "Update 2.4.0.Final";
+    }
+}
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 6de22a9..97aa4bd 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
@@ -2031,6 +2031,14 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
 
     @Override
     public ComponentModel addComponentModel(ComponentModel model) {
+        model = importComponentModel(model);
+        ComponentUtil.notifyCreated(session, this, model);
+
+        return model;
+    }
+
+    @Override
+    public ComponentModel importComponentModel(ComponentModel model) {
         ComponentFactory componentFactory = ComponentUtil.getComponentFactory(session, model);
         if (componentFactory == null) {
             throw new IllegalArgumentException("Invalid component type");
@@ -2057,8 +2065,6 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
         em.persist(c);
         setConfig(model, c);
         model.setId(c.getId());
-        ComponentUtil.notifyCreated(session, this, model);
-
         return model;
     }
 
diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
index cc4c9ff..588caf7 100644
--- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
@@ -37,4 +37,5 @@
     <include file="META-INF/jpa-changelog-2.1.0.xml"/>
     <include file="META-INF/jpa-changelog-2.2.0.xml"/>
     <include file="META-INF/jpa-changelog-2.3.0.xml"/>
+    <include file="META-INF/jpa-changelog-2.4.0.xml"/>
 </databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.4.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.4.0.xml
new file mode 100755
index 0000000..0aebd9a
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.4.0.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
+
+     <changeSet author="bburke@redhat.com" id="2.4.0">
+         <customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.PortLdapUserFedToComponentModel"/>
+     </changeSet>
+
+</databaseChangeLog>
\ No newline at end of file
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
index 8990fc4..194b2d5 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -37,4 +37,5 @@
     <include file="META-INF/jpa-changelog-2.1.0.xml"/>
     <include file="META-INF/jpa-changelog-2.2.0.xml"/>
     <include file="META-INF/jpa-changelog-2.3.0.xml"/>
+    <include file="META-INF/jpa-changelog-2.4.0.xml"/>
 </databaseChangeLog>
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java
index 3e429e2..c7f5be8 100755
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java
@@ -34,6 +34,7 @@ import org.keycloak.connections.mongo.updater.impl.updates.Update1_7_0;
 import org.keycloak.connections.mongo.updater.impl.updates.Update1_8_0;
 import org.keycloak.connections.mongo.updater.impl.updates.Update1_9_2;
 import org.keycloak.connections.mongo.updater.impl.updates.Update2_3_0;
+import org.keycloak.connections.mongo.updater.impl.updates.Update2_4_0;
 import org.keycloak.models.KeycloakSession;
 
 import java.util.Date;
@@ -59,7 +60,8 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
             Update1_7_0.class,
             Update1_8_0.class,
             Update1_9_2.class,
-            Update2_3_0.class
+            Update2_3_0.class,
+            Update2_4_0.class
     };
 
     @Override
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update2_4_0.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update2_4_0.java
new file mode 100644
index 0000000..f92eaaa
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update2_4_0.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.connections.mongo.updater.impl.updates;
+
+import com.mongodb.BasicDBList;
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import org.keycloak.keys.KeyProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.storage.UserStorageProvider;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class Update2_4_0 extends Update {
+
+    @Override
+    public String getId() {
+        return "2.4.0";
+    }
+
+    @Override
+    public void update(KeycloakSession session) {
+        portUserFedToComponent(LDAPConstants.LDAP_PROVIDER);
+        portUserFedMappersToComponent(LDAPConstants.LDAP_PROVIDER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper");
+    }
+
+    public void portUserFedToComponent(String providerId) {
+        DBCollection realms = db.getCollection("realms");
+        DBCursor cursor = realms.find();
+        while (cursor.hasNext()) {
+            BasicDBObject realm = (BasicDBObject) cursor.next();
+
+            String realmId = realm.getString("_id");
+            Set<String> removedProviders = new HashSet<>();
+
+            BasicDBList componentEntities = (BasicDBList) realm.get("componentEntities");
+            BasicDBList federationProviders = (BasicDBList) realm.get("userFederationProviders");
+            for (Object obj : federationProviders) {
+                BasicDBObject fedProvider = (BasicDBObject)obj;
+                if (fedProvider.getString("providerName").equals(providerId)) {
+                    String id = fedProvider.getString("id");
+                    int priority = fedProvider.getInt("priority");
+                    String displayName = fedProvider.getString("displayName");
+                    int fullSyncPeriod = fedProvider.getInt("fullSyncPeriod");
+                    int changedSyncPeriod = fedProvider.getInt("changedSyncPeriod");
+                    int lastSync = fedProvider.getInt("lastSync");
+                    BasicDBObject component = new BasicDBObject();
+                    component.put("id", id);
+                    component.put("name", displayName);
+                    component.put("providerType", UserStorageProvider.class.getName());
+                    component.put("providerId", providerId);
+                    component.put("parentId", realmId);
+
+                    BasicDBObject config = new BasicDBObject();
+                    config.put("priority", Collections.singletonList(Integer.toString(priority)));
+                    config.put("fullSyncPeriod", Collections.singletonList(Integer.toString(fullSyncPeriod)));
+                    config.put("changedSyncPeriod", Collections.singletonList(Integer.toString(changedSyncPeriod)));
+                    config.put("lastSync", Collections.singletonList(Integer.toString(lastSync)));
+
+                    BasicDBObject fedConfig = (BasicDBObject)fedProvider.get("config");
+                    if (fedConfig != null) {
+                        for (Map.Entry<String, Object> attr : new HashSet<>(fedConfig.entrySet())) {
+                            String attrName = attr.getKey();
+                            String attrValue = attr.getValue().toString();
+                            config.put(attrName, Collections.singletonList(attrValue));
+
+                        }
+                    }
+
+
+                    component.put("config", config);
+
+                    componentEntities.add(component);
+
+                }
+            }
+            Iterator<Object> it = federationProviders.iterator();
+            while (it.hasNext()) {
+                BasicDBObject fedProvider = (BasicDBObject)it.next();
+                String id = fedProvider.getString("id");
+                if (removedProviders.contains(id)) {
+                    it.remove();
+                }
+
+            }
+            realms.update(new BasicDBObject().append("_id", realmId), realm);
+        }
+    }
+    public void portUserFedMappersToComponent(String providerId, String mapperType) {
+        DBCollection realms = db.getCollection("realms");
+        DBCursor cursor = realms.find();
+        while (cursor.hasNext()) {
+            BasicDBObject realm = (BasicDBObject) cursor.next();
+
+            String realmId = realm.getString("_id");
+            Set<String> removedProviders = new HashSet<>();
+
+            BasicDBList componentEntities = (BasicDBList) realm.get("componentEntities");
+            BasicDBList federationProviders = (BasicDBList) realm.get("userFederationProviders");
+            BasicDBList fedMappers = (BasicDBList) realm.get("userFederationMappers");
+            for (Object obj : federationProviders) {
+                BasicDBObject fedProvider = (BasicDBObject)obj;
+                if (fedProvider.getString("providerName").equals(providerId)) {
+                    String id = fedProvider.getString("id");
+                    for (Object obj2 : fedMappers) {
+                        BasicDBObject fedMapper = (BasicDBObject)obj2;
+                        if (fedMapper.getString("federationProviderId").equals(id)) {
+                            String name = fedMapper.getString("name");
+                            String mapperId = fedMapper.getString("id");
+                            removedProviders.add(mapperId);
+                            String mapperProviderId = fedMapper.getString("federationMapperType");
+                            BasicDBObject component = new BasicDBObject();
+                            component.put("id", mapperId);
+                            component.put("name", name);
+                            component.put("providerType", mapperType);
+                            component.put("providerId", mapperProviderId);
+                            component.put("parentId", providerId);
+
+                            BasicDBObject fedConfig = (BasicDBObject)fedMapper.get("config");
+                            BasicDBObject config = new BasicDBObject();
+                            if (fedConfig != null) {
+                                for (Map.Entry<String, Object> attr : new HashSet<>(fedConfig.entrySet())) {
+                                    String attrName = attr.getKey();
+                                    String attrValue = attr.getValue().toString();
+                                    config.put(attrName, Collections.singletonList(attrValue));
+
+                                }
+                            }
+                            component.put("config", config);
+                            componentEntities.add(component);
+                        }
+                    }
+                }
+            }
+            Iterator<Object> it = fedMappers.iterator();
+            while (it.hasNext()) {
+                BasicDBObject fedMapper = (BasicDBObject)it.next();
+                String id = fedMapper.getString("id");
+                if (removedProviders.contains(id)) {
+                    it.remove();
+                }
+
+            }
+            realms.update(new BasicDBObject().append("_id", realmId), realm);
+        }
+    }
+}
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 205f6b3..a79c478 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
@@ -1954,6 +1954,13 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
 
     @Override
     public ComponentModel addComponentModel(ComponentModel model) {
+        model = importComponentModel(model);
+        ComponentUtil.notifyCreated(session, this, model);
+        return model;
+    }
+
+    @Override
+    public ComponentModel importComponentModel(ComponentModel model) {
         ComponentUtil.getComponentFactory(session, model).validateConfiguration(session, this, model);
 
         ComponentEntity entity = new ComponentEntity();
@@ -1970,7 +1977,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         }
         realm.getComponentEntities().add(entity);
         updateRealm();
-        ComponentUtil.notifyCreated(session, this, model);
         return model;
     }
 
diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
index aa8b440..e9df047 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
@@ -274,7 +274,22 @@ public interface RealmModel extends RoleContainerModel {
     public IdentityProviderMapperModel getIdentityProviderMapperByName(String brokerAlias, String name);
 
 
+    /**
+     * Adds component model.  Will call onCreate() method of ComponentFactory
+     *
+     * @param model
+     * @return
+     */
     ComponentModel addComponentModel(ComponentModel model);
+
+    /**
+     * Adds component model.  Will NOT call onCreate() method of ComponentFactory
+     *
+     * @param model
+     * @return
+     */
+    ComponentModel importComponentModel(ComponentModel model);
+
     void updateComponent(ComponentModel component);
     void removeComponent(ComponentModel component);
     void removeComponents(String parentId);
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index e35da61..bf4a6dc 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -352,19 +352,7 @@ public class ModelToRepresentation {
             }
         }
 
-        List<UserFederationProviderModel> fedProviderModels = realm.getUserFederationProviders();
-        if (fedProviderModels.size() > 0) {
-            List<UserFederationProviderRepresentation> fedProviderReps = new ArrayList<UserFederationProviderRepresentation>();
-            for (UserFederationProviderModel model : fedProviderModels) {
-                UserFederationProviderRepresentation fedProvRep = toRepresentation(model);
-                fedProviderReps.add(fedProvRep);
-            }
-            rep.setUserFederationProviders(fedProviderReps);
-        }
-
-        for (UserFederationMapperModel mapper : realm.getUserFederationMappers()) {
-            rep.addUserFederationMapper(toRepresentation(realm, mapper));
-        }
+        exportUserFederationProvidersAndMappers(realm, rep);
 
         for (IdentityProviderModel provider : realm.getIdentityProviders()) {
             rep.addIdentityProvider(toRepresentation(realm, provider));
@@ -396,6 +384,22 @@ public class ModelToRepresentation {
         return rep;
     }
 
+    public static void exportUserFederationProvidersAndMappers(RealmModel realm, RealmRepresentation rep) {
+        List<UserFederationProviderModel> fedProviderModels = realm.getUserFederationProviders();
+        if (fedProviderModels.size() > 0) {
+            List<UserFederationProviderRepresentation> fedProviderReps = new ArrayList<UserFederationProviderRepresentation>();
+            for (UserFederationProviderModel model : fedProviderModels) {
+                UserFederationProviderRepresentation fedProvRep = toRepresentation(model);
+                fedProviderReps.add(fedProvRep);
+            }
+            rep.setUserFederationProviders(fedProviderReps);
+        }
+
+        for (UserFederationMapperModel mapper : realm.getUserFederationMappers()) {
+            rep.addUserFederationMapper(toRepresentation(realm, mapper));
+        }
+    }
+
     public static void exportGroups(RealmModel realm, RealmRepresentation rep) {
         List<GroupRepresentation> groups = toGroupHierarchy(realm, true);
         rep.setGroups(groups);
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index c2e654e..8be4a9b 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -54,6 +54,7 @@ import org.keycloak.models.GroupModel;
 import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.LDAPConstants;
 import org.keycloak.models.ModelException;
 import org.keycloak.models.OTPPolicy;
 import org.keycloak.models.PasswordPolicy;
@@ -101,6 +102,8 @@ import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentatio
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.storage.UserStorageProviderModel;
 import org.keycloak.storage.federated.UserFederatedStorageProvider;
 import org.keycloak.util.JsonSerialization;
 
@@ -305,36 +308,8 @@ public class RepresentationToModel {
             String parentId = newRealm.getId();
             importComponents(newRealm, components, parentId);
         }
+        importUserFederationProvidersAndMappers(rep, newRealm);
 
-        List<UserFederationProviderModel> providerModels = null;
-        if (rep.getUserFederationProviders() != null) {
-            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));
-            }
-        }
 
         if (rep.getGroups() != null) {
             importGroups(newRealm, rep);
@@ -390,6 +365,64 @@ public class RepresentationToModel {
         }
     }
 
+    public static void importUserFederationProvidersAndMappers(RealmRepresentation rep, RealmModel newRealm) {
+        // providers to convert to component model
+        Set<String> convertSet = new HashSet<>();
+        convertSet.add(LDAPConstants.LDAP_PROVIDER);
+        Map<String, String> mapperConvertSet = new HashMap<>();
+        mapperConvertSet.put(LDAPConstants.LDAP_PROVIDER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper");
+
+
+        List<UserFederationProviderModel> providerModels = null;
+        Map<String, ComponentModel> userStorageModels = new HashMap<>();
+
+        if (rep.getUserFederationProviders() != null) {
+            providerModels = new LinkedList<>();
+            for (UserFederationProviderRepresentation fedRep : rep.getUserFederationProviders()) {
+                if (convertSet.contains(fedRep.getProviderName())) {
+                    ComponentModel component = convertFedProviderToComponent(newRealm.getId(), fedRep);
+                    userStorageModels.put(fedRep.getDisplayName(), newRealm.importComponentModel(component));
+                } else {
+                    providerModels.add(convertFederationProvider(fedRep));
+                }
+
+            }
+            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()) {
+                if (userStorageModels.containsKey(representation.getFederationProviderDisplayName())) {
+                    ComponentModel parent = userStorageModels.get(representation.getFederationProviderDisplayName());
+                    String newMapperType = mapperConvertSet.get(parent.getProviderId());
+                    ComponentModel mapper = convertFedMapperToComponent(newRealm, parent, representation, newMapperType);
+                    newRealm.importComponentModel(mapper);
+
+                } else {
+                    newRealm.addUserFederationMapper(toModel(newRealm, representation));
+                }
+            }
+        }
+    }
+
     protected static void importComponents(RealmModel newRealm, MultivaluedHashMap<String, ComponentExportRepresentation> components, String parentId) {
         for (Map.Entry<String, List<ComponentExportRepresentation>> entry : components.entrySet()) {
             String providerType = entry.getKey();
@@ -402,7 +435,7 @@ public class RepresentationToModel {
                 component.setProviderId(compRep.getProviderId());
                 component.setSubType(compRep.getSubType());
                 component.setParentId(parentId);
-                component = newRealm.addComponentModel(component);
+                component = newRealm.importComponentModel(component);
                 if (compRep.getSubComponents() != null) {
                     importComponents(newRealm, compRep.getSubComponents(), component.getId());
                 }
@@ -865,14 +898,53 @@ public class RepresentationToModel {
         List<UserFederationProviderModel> result = new ArrayList<UserFederationProviderModel>();
 
         for (UserFederationProviderRepresentation representation : providers) {
-            UserFederationProviderModel model = new UserFederationProviderModel(representation.getId(), representation.getProviderName(),
-                    representation.getConfig(), representation.getPriority(), representation.getDisplayName(),
-                    representation.getFullSyncPeriod(), representation.getChangedSyncPeriod(), representation.getLastSync());
+            UserFederationProviderModel model = convertFederationProvider(representation);
             result.add(model);
         }
         return result;
     }
 
+    private static UserFederationProviderModel convertFederationProvider(UserFederationProviderRepresentation representation) {
+        return new UserFederationProviderModel(representation.getId(), representation.getProviderName(),
+                        representation.getConfig(), representation.getPriority(), representation.getDisplayName(),
+                        representation.getFullSyncPeriod(), representation.getChangedSyncPeriod(), representation.getLastSync());
+    }
+
+    public static ComponentModel convertFedProviderToComponent(String realmId, UserFederationProviderRepresentation fedModel) {
+        UserStorageProviderModel model = new UserStorageProviderModel();
+        model.setId(fedModel.getId());
+        model.setName(fedModel.getDisplayName());
+        model.setParentId(realmId);
+        model.setProviderId(fedModel.getProviderName());
+        model.setProviderType(UserStorageProvider.class.getName());
+        model.setFullSyncPeriod(fedModel.getFullSyncPeriod());
+        model.setPriority(fedModel.getPriority());
+        model.setChangedSyncPeriod(fedModel.getChangedSyncPeriod());
+        model.setLastSync(fedModel.getLastSync());
+        if (fedModel.getConfig() != null) {
+            for (Map.Entry<String, String> entry : fedModel.getConfig().entrySet()) {
+                model.getConfig().putSingle(entry.getKey(), entry.getValue());
+            }
+        }
+        return model;
+    }
+
+    public static ComponentModel convertFedMapperToComponent(RealmModel realm, ComponentModel parent, UserFederationMapperRepresentation rep, String newMapperType) {
+        ComponentModel mapper = new ComponentModel();
+        mapper.setId(rep.getId());
+        mapper.setName(rep.getName());
+        mapper.setProviderId(rep.getFederationMapperType());
+        mapper.setProviderType(newMapperType);
+        mapper.setParentId(parent.getId());
+        if (rep.getConfig() != null) {
+            for (Map.Entry<String, String> entry : rep.getConfig().entrySet()) {
+                mapper.getConfig().putSingle(entry.getKey(), entry.getValue());
+            }
+        }
+        return mapper;
+    }
+
+
     public static UserFederationMapperModel toModel(RealmModel realm, UserFederationMapperRepresentation rep) {
         UserFederationMapperModel model = new UserFederationMapperModel();
         model.setId(rep.getId());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java
index 2809b97..11ed2f6 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java
@@ -48,7 +48,9 @@ import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.OAuthClient;
 import org.keycloak.testsuite.federation.ldap.FederationTestUtils;
@@ -61,8 +63,10 @@ import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.LDAPRule;
 import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.JsonSerialization;
 import org.openqa.selenium.WebDriver;
 
+import java.io.FileOutputStream;
 import java.util.List;
 import java.util.Map;
 
@@ -104,6 +108,20 @@ public class FederationProvidersIntegrationTest {
         }
     });
 
+    /*
+    @Test
+    public void exportJson() throws Exception {
+        KeycloakSession session = keycloakRule.startSession();
+        FileOutputStream os = new FileOutputStream("/Users/williamburke/jboss/keycloak/p1b-repo/keycloak/fed-provider.json");
+        RealmManager manager = new RealmManager(session);
+        RealmModel appRealm = manager.getRealm("test");
+        RealmRepresentation rep = ModelToRepresentation.toRepresentation(appRealm, true);
+        JsonSerialization.writeValueToStream(os, rep);
+        os.close();
+
+    }
+    */
+
     @ClassRule
     public static TestRule chain = RuleChain
             .outerRule(ldapRule)
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPLegacyImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPLegacyImportTest.java
new file mode 100755
index 0000000..fc34c86
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPLegacyImportTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.federation.storage.ldap;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runners.MethodSorters;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.credential.CredentialModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.ModelReadOnlyException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.storage.UserStorageProviderModel;
+import org.keycloak.storage.ldap.LDAPConfig;
+import org.keycloak.storage.ldap.LDAPStorageProvider;
+import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
+import org.keycloak.storage.ldap.idm.model.LDAPObject;
+import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapper;
+import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapperFactory;
+import org.keycloak.storage.ldap.mappers.HardcodedLDAPRoleStorageMapper;
+import org.keycloak.storage.ldap.mappers.HardcodedLDAPRoleStorageMapperFactory;
+import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
+import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapper;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.pages.AccountPasswordPage;
+import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
+import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.RegisterPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.LDAPRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.JsonSerialization;
+import org.openqa.selenium.WebDriver;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests that legacy UserFederationProvider json export is converted to ComponentModel
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class LDAPLegacyImportTest {
+
+    private static LDAPRule ldapRule = new LDAPRule();
+
+    private static ComponentModel ldapModel = null;
+
+
+    private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+
+        @Override
+        public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+            LDAPTestUtils.addLocalUser(manager.getSession(), appRealm, "marykeycloak", "mary@test.com", "password-app");
+
+            RealmRepresentation imported = null;
+            try {
+                imported = JsonSerialization.readValue(getClass().getResourceAsStream("/ldap/fed-provider-export.json"), RealmRepresentation.class);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+            RepresentationToModel.importUserFederationProvidersAndMappers(imported, appRealm);
+            ldapModel = appRealm.getComponents(appRealm.getId(), UserStorageProvider.class.getName()).get(0);
+            // Delete all LDAP users and add some new for testing
+            LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
+            LDAPTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm);
+
+            LDAPObject john = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
+            LDAPTestUtils.updateLDAPPassword(ldapFedProvider, john, "Password1");
+
+            LDAPObject existing = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "existing", "Existing", "Foo", "existing@email.org", null, "5678");
+
+            appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
+        }
+    });
+
+    @ClassRule
+    public static TestRule chain = RuleChain
+            .outerRule(ldapRule)
+            .around(keycloakRule);
+
+    @Rule
+    public WebRule webRule = new WebRule(this);
+
+    @WebResource
+    protected OAuthClient oauth;
+
+    @WebResource
+    protected WebDriver driver;
+
+    @WebResource
+    protected AppPage appPage;
+
+    @WebResource
+    protected RegisterPage registerPage;
+
+    @WebResource
+    protected LoginPage loginPage;
+
+    @WebResource
+    protected AccountUpdateProfilePage profilePage;
+
+    @WebResource
+    protected AccountPasswordPage changePasswordPage;
+
+    //@Test
+    public void runit() throws Exception {
+        Thread.sleep(10000000);
+
+    }
+
+    private void loginSuccessAndLogout(String username, String password) {
+        loginPage.open();
+        loginPage.login(username, password);
+        Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+        Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+        oauth.openLogout();
+    }
+
+
+    @Test
+    public void loginClassic() {
+        loginPage.open();
+        loginPage.login("marykeycloak", "password-app");
+
+        Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+        Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+
+    }
+
+    @Test
+    public void loginLdap() {
+        loginPage.open();
+        loginPage.login("johnkeycloak", "Password1");
+
+        Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+        Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+
+        profilePage.open();
+        Assert.assertEquals("John", profilePage.getFirstName());
+        Assert.assertEquals("Doe", profilePage.getLastName());
+        Assert.assertEquals("john@email.org", profilePage.getEmail());
+    }
+
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
index 6e79d69..a2b2636 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
@@ -132,7 +132,7 @@ public class UserStorageTest {
 
     }
 
-    @Test
+    //@Test
     public void testIDE() throws Exception {
         Thread.sleep(100000000);
     }
diff --git a/testsuite/integration/src/test/resources/ldap/fed-provider-export.json b/testsuite/integration/src/test/resources/ldap/fed-provider-export.json
new file mode 100644
index 0000000..4bbe374
--- /dev/null
+++ b/testsuite/integration/src/test/resources/ldap/fed-provider-export.json
@@ -0,0 +1,622 @@
+{
+  "id": "test",
+  "realm": "test",
+  "notBefore": 0,
+  "revokeRefreshToken": false,
+  "accessTokenLifespan": 300,
+  "accessTokenLifespanForImplicitFlow": 900,
+  "ssoSessionIdleTimeout": 1800,
+  "ssoSessionMaxLifespan": 36000,
+  "offlineSessionIdleTimeout": 2592000,
+  "accessCodeLifespan": 60,
+  "accessCodeLifespanUserAction": 300,
+  "accessCodeLifespanLogin": 1800,
+  "enabled": true,
+  "sslRequired": "external",
+  "registrationAllowed": true,
+  "registrationEmailAsUsername": false,
+  "rememberMe": false,
+  "verifyEmail": false,
+  "resetPasswordAllowed": true,
+  "editUsernameAllowed": true,
+  "bruteForceProtected": false,
+  "maxFailureWaitSeconds": 900,
+  "minimumQuickLoginWaitSeconds": 60,
+  "waitIncrementSeconds": 60,
+  "quickLoginCheckMilliSeconds": 1000,
+  "maxDeltaTimeSeconds": 43200,
+  "failureFactor": 30,
+  "groups": [
+    {
+      "id": "2aa57ddd-e48f-4a62-bb8e-53ebe2ff1057",
+      "name": "topGroup",
+      "path": "/topGroup",
+      "attributes": {
+        "topAttribute": [
+          "true"
+        ]
+      },
+      "realmRoles": [
+        "user"
+      ],
+      "clientRoles": {},
+      "subGroups": [
+        {
+          "id": "8e91afd4-b8e4-4de4-ba37-1edc7298d518",
+          "name": "level2group",
+          "path": "/topGroup/level2group",
+          "attributes": {
+            "level2Attribute": [
+              "true"
+            ]
+          },
+          "realmRoles": [
+            "admin"
+          ],
+          "clientRoles": {
+            "test-app": [
+              "customer-user"
+            ]
+          },
+          "subGroups": []
+        }
+      ]
+    }
+  ],
+  "defaultRoles": [
+    "user",
+    "offline_access",
+    "uma_authorization"
+  ],
+  "requiredCredentials": [
+    "password"
+  ],
+  "passwordPolicy": "hashIterations(20000)",
+  "otpPolicyType": "totp",
+  "otpPolicyAlgorithm": "HmacSHA1",
+  "otpPolicyInitialCounter": 0,
+  "otpPolicyDigits": 6,
+  "otpPolicyLookAheadWindow": 1,
+  "otpPolicyPeriod": 30,
+  "browserSecurityHeaders": {
+    "xContentTypeOptions": "nosniff",
+    "xFrameOptions": "SAMEORIGIN",
+    "contentSecurityPolicy": "frame-src 'self'"
+  },
+  "smtpServer": {
+    "host": "localhost",
+    "from": "auto@keycloak.org",
+    "port": "3025"
+  },
+  "userFederationProviders": [
+    {
+      "id": "1fc3afd2-4c18-48dd-9055-b4bbae9229b7",
+      "displayName": "test-ldap",
+      "providerName": "ldap",
+      "config": {
+        "serverPrincipal": "HTTP/localhost@KEYCLOAK.ORG",
+        "debug": "true",
+        "pagination": "true",
+        "keyTab": "/Users/williamburke/jboss/keycloak/p1b-repo/keycloak/testsuite/integration/target/test-classes/kerberos/http.keytab",
+        "connectionPooling": "true",
+        "usersDn": "ou=People,dc=keycloak,dc=org",
+        "useKerberosForPasswordAuthentication": "false",
+        "kerberosRealm": "KEYCLOAK.ORG",
+        "bindCredential": "secret",
+        "bindDn": "uid=admin,ou=system",
+        "allowPasswordAuthentication": "true",
+        "vendor": "other",
+        "editMode": "WRITABLE",
+        "allowKerberosAuthentication": "false",
+        "connectionUrl": "ldap://localhost:10389",
+        "syncRegistrations": "true",
+        "baseDn": "dc=keycloak,dc=org",
+        "batchSizeForSync": "3",
+        "updateProfileFirstLogin": "true"
+      },
+      "priority": 0,
+      "fullSyncPeriod": -1,
+      "changedSyncPeriod": -1,
+      "lastSync": 0
+    }
+  ],
+  "userFederationMappers": [
+    {
+      "id": "b2fc2d9c-2ea8-417f-96db-2565be62a646",
+      "name": "last name",
+      "federationProviderDisplayName": "test-ldap",
+      "federationMapperType": "user-attribute-ldap-mapper",
+      "config": {
+        "always.read.value.from.ldap": "true",
+        "read.only": "false",
+        "ldap.attribute": "sn",
+        "is.mandatory.in.ldap": "true",
+        "user.model.attribute": "lastName"
+      }
+    },
+    {
+      "id": "6dc25318-dc20-4927-ba19-9293ab31aa28",
+      "name": "zipCodeMapper",
+      "federationProviderDisplayName": "test-ldap",
+      "federationMapperType": "user-attribute-ldap-mapper",
+      "config": {
+        "always.read.value.from.ldap": "false",
+        "read.only": "false",
+        "ldap.attribute": "postalCode",
+        "is.mandatory.in.ldap": "false",
+        "user.model.attribute": "postal_code"
+      }
+    },
+    {
+      "id": "7afa12a2-f36e-4f87-b715-e941773c8534",
+      "name": "username",
+      "federationProviderDisplayName": "test-ldap",
+      "federationMapperType": "user-attribute-ldap-mapper",
+      "config": {
+        "always.read.value.from.ldap": "false",
+        "read.only": "false",
+        "ldap.attribute": "uid",
+        "is.mandatory.in.ldap": "true",
+        "user.model.attribute": "username"
+      }
+    },
+    {
+      "id": "abfe054c-6d2a-4870-a239-1a312c3e5a94",
+      "name": "creation date",
+      "federationProviderDisplayName": "test-ldap",
+      "federationMapperType": "user-attribute-ldap-mapper",
+      "config": {
+        "always.read.value.from.ldap": "true",
+        "read.only": "true",
+        "ldap.attribute": "createTimestamp",
+        "is.mandatory.in.ldap": "false",
+        "user.model.attribute": "createTimestamp"
+      }
+    },
+    {
+      "id": "6aef95e5-736e-4b1e-98d0-332f61f94ff9",
+      "name": "first name",
+      "federationProviderDisplayName": "test-ldap",
+      "federationMapperType": "user-attribute-ldap-mapper",
+      "config": {
+        "always.read.value.from.ldap": "true",
+        "read.only": "false",
+        "ldap.attribute": "cn",
+        "is.mandatory.in.ldap": "true",
+        "user.model.attribute": "firstName"
+      }
+    },
+    {
+      "id": "0601e4a2-fd63-4f6a-ae3b-13cc6f4f4f1c",
+      "name": "email",
+      "federationProviderDisplayName": "test-ldap",
+      "federationMapperType": "user-attribute-ldap-mapper",
+      "config": {
+        "always.read.value.from.ldap": "false",
+        "read.only": "false",
+        "ldap.attribute": "mail",
+        "is.mandatory.in.ldap": "false",
+        "user.model.attribute": "email"
+      }
+    },
+    {
+      "id": "fa308910-3be9-4bd8-8256-66cf04d8fcd2",
+      "name": "modify date",
+      "federationProviderDisplayName": "test-ldap",
+      "federationMapperType": "user-attribute-ldap-mapper",
+      "config": {
+        "always.read.value.from.ldap": "true",
+        "read.only": "true",
+        "ldap.attribute": "modifyTimestamp",
+        "is.mandatory.in.ldap": "false",
+        "user.model.attribute": "modifyTimestamp"
+      }
+    }
+  ],
+  "eventsEnabled": false,
+  "eventsListeners": [
+    "jboss-logging"
+  ],
+  "enabledEventTypes": [],
+  "adminEventsEnabled": false,
+  "adminEventsDetailsEnabled": false,
+  "internationalizationEnabled": true,
+  "supportedLocales": [
+    "de",
+    "en"
+  ],
+  "defaultLocale": "en",
+  "authenticationFlows": [
+    {
+      "id": "b12463a9-5d33-4f27-b010-4005db77e602",
+      "alias": "Handle Existing Account",
+      "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "idp-confirm-link",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "idp-email-verification",
+          "requirement": "ALTERNATIVE",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "ALTERNATIVE",
+          "priority": 30,
+          "flowAlias": "Verify Existing Account by Re-authentication",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "c1684fc8-a99d-4e19-a795-478e4d793fb5",
+      "alias": "Verify Existing Account by Re-authentication",
+      "description": "Reauthentication of existing account",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "idp-username-password-form",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "auth-otp-form",
+          "requirement": "OPTIONAL",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "09af30d8-8c2a-45a4-a2be-b7617e9d0185",
+      "alias": "browser",
+      "description": "browser based authentication",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "auth-cookie",
+          "requirement": "ALTERNATIVE",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "auth-spnego",
+          "requirement": "DISABLED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "identity-provider-redirector",
+          "requirement": "ALTERNATIVE",
+          "priority": 25,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "ALTERNATIVE",
+          "priority": 30,
+          "flowAlias": "forms",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "6cdf31d0-9c91-4ea6-8e37-da6e8fa7544c",
+      "alias": "clients",
+      "description": "Base authentication for clients",
+      "providerId": "client-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "client-secret",
+          "requirement": "ALTERNATIVE",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "client-jwt",
+          "requirement": "ALTERNATIVE",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "c9a38de8-4c0c-496a-9936-b9753f73bfcc",
+      "alias": "direct grant",
+      "description": "OpenID Connect Resource Owner Grant",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "direct-grant-validate-username",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "direct-grant-validate-password",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "direct-grant-validate-otp",
+          "requirement": "OPTIONAL",
+          "priority": 30,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "3755e297-7907-4c14-8c5f-d77e2bfe4b5d",
+      "alias": "first broker login",
+      "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticatorConfig": "review profile config",
+          "authenticator": "idp-review-profile",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticatorConfig": "create unique user config",
+          "authenticator": "idp-create-user-if-unique",
+          "requirement": "ALTERNATIVE",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "ALTERNATIVE",
+          "priority": 30,
+          "flowAlias": "Handle Existing Account",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "f35b2f00-3e84-4f2e-b48e-3e4159d88a06",
+      "alias": "forms",
+      "description": "Username, password, otp and other auth forms.",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "auth-username-password-form",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "auth-otp-form",
+          "requirement": "OPTIONAL",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "441b4480-1ace-483a-bffb-f0cb6659fe32",
+      "alias": "registration",
+      "description": "registration flow",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "registration-page-form",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "flowAlias": "registration form",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "c7de2a37-29a1-471a-9b51-699a69032b00",
+      "alias": "registration form",
+      "description": "registration form",
+      "providerId": "form-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "registration-user-creation",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "registration-profile-action",
+          "requirement": "REQUIRED",
+          "priority": 40,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "registration-password-action",
+          "requirement": "REQUIRED",
+          "priority": 50,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "registration-recaptcha-action",
+          "requirement": "DISABLED",
+          "priority": 60,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "d362be0a-df20-4ce7-9288-f8448e0c4647",
+      "alias": "reset credentials",
+      "description": "Reset credentials for a user if they forgot their password or something",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "reset-credentials-choose-user",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "reset-credential-email",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "reset-password",
+          "requirement": "REQUIRED",
+          "priority": 30,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "reset-otp",
+          "requirement": "OPTIONAL",
+          "priority": 40,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "c2d7a1ae-57c9-4f3b-a4ce-55c3f0d9869f",
+      "alias": "saml ecp",
+      "description": "SAML ECP Profile Authentication Flow",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "http-basic-authenticator",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    }
+  ],
+  "authenticatorConfig": [
+    {
+      "id": "a2490828-becb-435f-9c3c-318b3939bf64",
+      "alias": "create unique user config",
+      "config": {
+        "require.password.update.after.registration": "false"
+      }
+    },
+    {
+      "id": "78421671-f733-4901-82bc-58bf50c43206",
+      "alias": "review profile config",
+      "config": {
+        "update.profile.on.first.login": "missing"
+      }
+    }
+  ],
+  "requiredActions": [
+    {
+      "alias": "CONFIGURE_TOTP",
+      "name": "Configure OTP",
+      "providerId": "CONFIGURE_TOTP",
+      "enabled": true,
+      "defaultAction": false,
+      "config": {}
+    },
+    {
+      "alias": "UPDATE_PASSWORD",
+      "name": "Update Password",
+      "providerId": "UPDATE_PASSWORD",
+      "enabled": true,
+      "defaultAction": false,
+      "config": {}
+    },
+    {
+      "alias": "UPDATE_PROFILE",
+      "name": "Update Profile",
+      "providerId": "UPDATE_PROFILE",
+      "enabled": true,
+      "defaultAction": false,
+      "config": {}
+    },
+    {
+      "alias": "VERIFY_EMAIL",
+      "name": "Verify Email",
+      "providerId": "VERIFY_EMAIL",
+      "enabled": true,
+      "defaultAction": false,
+      "config": {}
+    },
+    {
+      "alias": "terms_and_conditions",
+      "name": "Terms and Conditions",
+      "providerId": "terms_and_conditions",
+      "enabled": false,
+      "defaultAction": false,
+      "config": {}
+    }
+  ],
+  "browserFlow": "browser",
+  "registrationFlow": "registration",
+  "directGrantFlow": "direct grant",
+  "resetCredentialsFlow": "reset credentials",
+  "clientAuthenticationFlow": "clients",
+  "attributes": {
+    "_browser_header.xFrameOptions": "SAMEORIGIN",
+    "failureFactor": "30",
+    "quickLoginCheckMilliSeconds": "1000",
+    "maxDeltaTimeSeconds": "43200",
+    "_browser_header.xContentTypeOptions": "nosniff",
+    "bruteForceProtected": "false",
+    "maxFailureWaitSeconds": "900",
+    "_browser_header.contentSecurityPolicy": "frame-src 'self'",
+    "minimumQuickLoginWaitSeconds": "60",
+    "waitIncrementSeconds": "60"
+  }
+}
\ No newline at end of file