keycloak-uncached

Changes

server-spi/src/main/java/org/keycloak/storage/changeset/UserDataQuery.java 46(+0 -46)

Details

diff --git a/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java b/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java
index fd8af9f..fc0e47f 100644
--- a/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java
+++ b/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java
@@ -119,6 +119,7 @@ public class KeycloakInstalled {
                 .queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
                 .queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
                 .queryParam(OAuth2Constants.STATE, state)
+                .queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
                 .build().toString();
 
         Desktop.getDesktop().browse(new URI(authUrl));
@@ -175,6 +176,7 @@ public class KeycloakInstalled {
                 .queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
                 .queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
                 .queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
+                .queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
                 .build().toString();
 
         printer.println("Open the following URL in a browser. After login copy/paste the code back and press <enter>");
diff --git a/core/src/main/java/org/keycloak/representations/idm/ComponentRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ComponentRepresentation.java
new file mode 100755
index 0000000..bbc4a42
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/ComponentRepresentation.java
@@ -0,0 +1,101 @@
+/*
+ * 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.representations.idm;
+
+import org.keycloak.common.util.MultivaluedHashMap;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ComponentRepresentation {
+
+    private String id;
+    private String name;
+    private String providerId;
+    private String providerType;
+    private String parentId;
+    private MultivaluedHashMap<String, String> config;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getProviderId() {
+        return providerId;
+    }
+
+    public void setProviderId(String providerId) {
+        this.providerId = providerId;
+    }
+
+    public String getProviderType() {
+        return providerType;
+    }
+
+    public void setProviderType(String providerType) {
+        this.providerType = providerType;
+    }
+
+    public String getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(String parentId) {
+        this.parentId = parentId;
+    }
+
+    public MultivaluedHashMap<String, String> getConfig() {
+        return config;
+    }
+
+    public void setConfig(MultivaluedHashMap<String, String> config) {
+        this.config = config;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ComponentRepresentation that = (ComponentRepresentation) o;
+
+        if (!id.equals(that.id)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/ComponentTypeRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ComponentTypeRepresentation.java
new file mode 100644
index 0000000..8ba0da6
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/ComponentTypeRepresentation.java
@@ -0,0 +1,57 @@
+/*
+ * 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.representations.idm;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ComponentTypeRepresentation {
+    protected String id;
+    protected String helpText;
+    protected List<ConfigPropertyRepresentation> properties;
+
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getHelpText() {
+        return helpText;
+    }
+
+    public void setHelpText(String helpText) {
+        this.helpText = helpText;
+    }
+
+    public List<ConfigPropertyRepresentation> getProperties() {
+        return properties;
+    }
+
+    public void setProperties(List<ConfigPropertyRepresentation> properties) {
+        this.properties = properties;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java b/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java
index c3aeeeb..59d400e 100755
--- a/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.representations.info;
 
+import org.keycloak.representations.idm.ComponentTypeRepresentation;
 import org.keycloak.representations.idm.PasswordPolicyTypeRepresentation;
 import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.representations.idm.ProtocolMapperTypeRepresentation;
@@ -43,6 +44,7 @@ public class ServerInfoRepresentation {
     private Map<String, List<ProtocolMapperTypeRepresentation>> protocolMapperTypes;
     private Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers;
     private Map<String, List<ClientInstallationRepresentation>> clientInstallations;
+    private Map<String, List<ComponentTypeRepresentation>> componentTypes;
 
     private List<PasswordPolicyTypeRepresentation> passwordPolicies;
 
@@ -144,4 +146,11 @@ public class ServerInfoRepresentation {
         this.passwordPolicies = passwordPolicies;
     }
 
+    public Map<String, List<ComponentTypeRepresentation>> getComponentTypes() {
+        return componentTypes;
+    }
+
+    public void setComponentTypes(Map<String, List<ComponentTypeRepresentation>> componentTypes) {
+        this.componentTypes = componentTypes;
+    }
 }
diff --git a/examples/providers/pom.xml b/examples/providers/pom.xml
index 20b4eaa..fcde8b0 100755
--- a/examples/providers/pom.xml
+++ b/examples/providers/pom.xml
@@ -37,5 +37,6 @@
         <module>authenticator</module>
         <module>rest</module>
         <module>domain-extension</module>
+        <module>user-storage-jpa</module>
     </modules>
 </project>
diff --git a/examples/providers/user-storage-jpa/pom.xml b/examples/providers/user-storage-jpa/pom.xml
new file mode 100755
index 0000000..b025900
--- /dev/null
+++ b/examples/providers/user-storage-jpa/pom.xml
@@ -0,0 +1,79 @@
+<!--
+  ~ 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-examples-providers-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>2.1.0-SNAPSHOT</version>
+    </parent>
+
+    <name>Properties Authentication Provider Example</name>
+    <description/>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>user-storage-jpa-example</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-server-spi</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-jpa</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate.javax.persistence</groupId>
+            <artifactId>hibernate-jpa-2.1-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-entitymanager</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>user-storage-jpa-example</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/ExampleUserStorageProvider.java b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/ExampleUserStorageProvider.java
new file mode 100644
index 0000000..0705333
--- /dev/null
+++ b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/ExampleUserStorageProvider.java
@@ -0,0 +1,118 @@
+/*
+ * 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.examples.storage.user;
+
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.storage.StorageId;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.storage.user.UserLookupProvider;
+import org.keycloak.storage.user.UserRegistrationProvider;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ExampleUserStorageProvider implements UserStorageProvider, UserLookupProvider, UserRegistrationProvider {
+    protected EntityManager em;
+    protected ComponentModel model;
+    protected KeycloakSession session;
+
+    public ExampleUserStorageProvider(EntityManager em, ComponentModel model, KeycloakSession session) {
+        this.em = em;
+        this.model = model;
+        this.session = session;
+    }
+
+    @Override
+    public void preRemove(RealmModel realm) {
+
+    }
+
+    @Override
+    public void preRemove(RealmModel realm, GroupModel group) {
+
+    }
+
+    @Override
+    public void preRemove(RealmModel realm, RoleModel role) {
+
+    }
+
+    @Override
+    public void close() {
+        em.close();
+    }
+
+    @Override
+    public UserModel getUserById(String id, RealmModel realm) {
+        String persistenceId = StorageId.externalId(id);
+        UserEntity entity = em.find(UserEntity.class, persistenceId);
+        if (entity == null) return null;
+        return new UserAdapter(session, realm, model, entity);
+    }
+
+    @Override
+    public UserModel getUserByUsername(String username, RealmModel realm) {
+        TypedQuery<UserEntity> query = em.createNamedQuery("getUserByUsername", UserEntity.class);
+        query.setParameter("username", username);
+        List<UserEntity> result = query.getResultList();
+        if (result.isEmpty()) return null;
+        return new UserAdapter(session, realm, model, result.get(0));
+    }
+
+    @Override
+    public UserModel getUserByEmail(String email, RealmModel realm) {
+        TypedQuery<UserEntity> query = em.createNamedQuery("getUserByEmail", UserEntity.class);
+        query.setParameter("email", email);
+        List<UserEntity> result = query.getResultList();
+        if (result.isEmpty()) return null;
+        return new UserAdapter(session, realm, model, result.get(0));
+    }
+
+    @Override
+    public UserModel addUser(RealmModel realm, String username) {
+        UserEntity entity = new UserEntity();
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setUsername(username);
+        em.persist(entity);
+        return new UserAdapter(session, realm, model, entity);
+    }
+
+    @Override
+    public boolean removeUser(RealmModel realm, UserModel user) {
+        String persistenceId = StorageId.externalId(user.getId());
+        UserEntity entity = em.find(UserEntity.class, persistenceId);
+        if (entity == null) return false;
+        em.remove(entity);
+        return true;
+    }
+
+    @Override
+    public void grantToAllUsers(RealmModel realm, RoleModel role) {
+
+    }
+}
diff --git a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/ExampleUserStorageProviderFactory.java b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/ExampleUserStorageProviderFactory.java
new file mode 100644
index 0000000..963a558
--- /dev/null
+++ b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/ExampleUserStorageProviderFactory.java
@@ -0,0 +1,60 @@
+/*
+ * 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.examples.storage.user;
+
+import org.keycloak.Config;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.connections.jpa.JndiEntityManagerLookup;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.storage.UserStorageProviderFactory;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ExampleUserStorageProviderFactory implements UserStorageProviderFactory<ExampleUserStorageProvider> {
+    protected String jndiName = "java:jboss/ExampleUserEntityManagerFactory";
+
+    @Override
+    public ExampleUserStorageProvider create(KeycloakSession session, ComponentModel model) {
+        EntityManager em = JndiEntityManagerLookup.getSessionEntityManager(session, jndiName);
+        return new ExampleUserStorageProvider(em, model, session);
+    }
+
+    @Override
+    public String getId() {
+        return "example-user-storage";
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/UserAdapter.java b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/UserAdapter.java
new file mode 100644
index 0000000..f851904
--- /dev/null
+++ b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/UserAdapter.java
@@ -0,0 +1,135 @@
+/*
+ * 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.examples.storage.user;
+
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.storage.StorageId;
+import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserAdapter extends AbstractUserAdapterFederatedStorage {
+    protected UserEntity entity;
+    protected String keycloakId;
+
+    public UserAdapter(KeycloakSession session, RealmModel realm, ComponentModel model, UserEntity entity) {
+        super(session, realm, model);
+        this.entity = entity;
+        keycloakId = StorageId.keycloakId(model, entity.getId());
+    }
+
+    @Override
+    public String getUsername() {
+        return entity.getUsername();
+    }
+
+    @Override
+    public void setUsername(String username) {
+        entity.setUsername(username);
+
+    }
+
+    @Override
+    public void setEmail(String email) {
+        entity.setEmail(email);
+    }
+
+    @Override
+    public String getEmail() {
+        return entity.getEmail();
+    }
+
+    @Override
+    public String getId() {
+        return keycloakId;
+    }
+
+    @Override
+    public void updateCredential(UserCredentialModel cred) {
+        if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+            entity.setPassword(cred.getValue());
+        } else {
+            super.updateCredential(cred);
+        }
+    }
+
+    @Override
+    public void setSingleAttribute(String name, String value) {
+        if (name.equals("phone")) {
+            entity.setPhone(value);
+        } else {
+            super.setSingleAttribute(name, value);
+        }
+    }
+
+    @Override
+    public void removeAttribute(String name) {
+        if (name.equals("phone")) {
+            entity.setPhone(null);
+        } else {
+            super.removeAttribute(name);
+        }
+    }
+
+    @Override
+    public void setAttribute(String name, List<String> values) {
+        if (name.equals("phone")) {
+            entity.setPhone(values.get(0));
+        } else {
+            super.setAttribute(name, values);
+        }
+    }
+
+    @Override
+    public String getFirstAttribute(String name) {
+        if (name.equals("phone")) {
+            return entity.getPhone();
+        } else {
+            return super.getFirstAttribute(name);
+        }
+    }
+
+    @Override
+    public Map<String, List<String>> getAttributes() {
+        Map<String, List<String>> attrs = super.getAttributes();
+        MultivaluedHashMap<String, String> all = new MultivaluedHashMap<>();
+        all.putAll(attrs);
+        all.add("phone", entity.getPhone());
+        return all;
+    }
+
+    @Override
+    public List<String> getAttribute(String name) {
+        if (name.equals("phone")) {
+            List<String> phone = new LinkedList<>();
+            phone.add(entity.getPhone());
+            return phone;
+        } else {
+            return super.getAttribute(name);
+        }
+    }
+}
diff --git a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/UserEntity.java b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/UserEntity.java
new file mode 100644
index 0000000..c9227d2
--- /dev/null
+++ b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/UserEntity.java
@@ -0,0 +1,82 @@
+/*
+ * 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.examples.storage.user;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+        @NamedQuery(name="getUserByUsername", query="select u from UserEntity u where u.username = :username"),
+        @NamedQuery(name="getUserByEmail", query="select u from UserEntity u where u.email = :email"),
+})
+@Entity
+public class UserEntity {
+    @Id
+    private String id;
+
+
+    private String username;
+    private String email;
+    private String password;
+    private String phone;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+}
diff --git a/examples/providers/user-storage-jpa/src/main/resources/META-INF/persistence.xml b/examples/providers/user-storage-jpa/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..5af362d
--- /dev/null
+++ b/examples/providers/user-storage-jpa/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<persistence version="2.0"
+             xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+             xsi:schemaLocation="
+        http://java.sun.com/xml/ns/persistence
+        http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
+    <persistence-unit name="primary">
+        <non-jta-data-source>java:jboss/datasources/ExampleUserDS</non-jta-data-source>
+
+        <class>org.keycloak.examples.storage.user.UserEntity</class>
+
+        <properties>
+            <property name="jboss.entity.manager.factory.jndi.name" value="java:jboss/ExampleUserEntityManagerFactory" />
+            <property name="hibernate.hbm2ddl.auto" value="update" />
+            <property name="hibernate.show_sql" value="false" />
+        </properties>
+    </persistence-unit>
+</persistence>
diff --git a/examples/providers/user-storage-jpa/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory b/examples/providers/user-storage-jpa/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
new file mode 100644
index 0000000..1634d11
--- /dev/null
+++ b/examples/providers/user-storage-jpa/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
@@ -0,0 +1 @@
+org.keycloak.examples.storage.user.ExampleUserStorageProviderFactory
\ No newline at end of file
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/TxAwareLDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/TxAwareLDAPUserModelDelegate.java
index 622ebb6..9126a66 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/TxAwareLDAPUserModelDelegate.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/TxAwareLDAPUserModelDelegate.java
@@ -70,7 +70,7 @@ public abstract class TxAwareLDAPUserModelDelegate extends UserModelDelegate {
                 logger.trace("Starting and enlisting transaction for object " + ldapUser.getDn().toString());
             }
 
-            this.provider.getSession().getTransaction().enlistAfterCompletion(transaction);
+            this.provider.getSession().getTransactionManager().enlistAfterCompletion(transaction);
         }
     }
 
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java
index af910a7..b577326 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java
@@ -163,7 +163,7 @@ public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMap
 
             UserModel that = session.userStorage().getUserByEmail(email, realm);
             if (that != null && !that.getId().equals(user.getId())) {
-                session.getTransaction().setRollbackOnly();
+                session.getTransactionManager().setRollbackOnly();
                 String exceptionMessage = String.format("Can't import user '%s' from LDAP because email '%s' already exists in Keycloak. Existing user with this email is '%s'", user.getUsername(), email, that.getUsername());
                 throw new ModelDuplicateException(exceptionMessage, UserModel.EMAIL);
             }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
index cea4388..56a385f 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
@@ -40,7 +40,7 @@ public class InfinispanStoreFactoryProvider implements CachedStoreFactoryProvide
     InfinispanStoreFactoryProvider(KeycloakSession delegate) {
         this.session = delegate;
         this.transaction = new CacheTransaction();
-        this.session.getTransaction().enlistAfterCompletion(transaction);
+        this.session.getTransactionManager().enlistAfterCompletion(transaction);
     }
 
     @Override
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
index 6dcc0d9..80997c3 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
@@ -18,6 +18,7 @@
 package org.keycloak.models.cache.infinispan.entities;
 
 import org.keycloak.common.enums.SslRequired;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.AuthenticatorConfigModel;
@@ -29,20 +30,14 @@ import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.OTPPolicy;
 import org.keycloak.models.PasswordPolicy;
 import org.keycloak.models.RealmModel;
-import org.keycloak.models.RealmProvider;
 import org.keycloak.models.RequiredActionProviderModel;
 import org.keycloak.models.RequiredCredentialModel;
-import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserFederationMapperModel;
 import org.keycloak.models.UserFederationProviderModel;
-import org.keycloak.models.cache.infinispan.RealmCache;
 import org.keycloak.common.util.MultivaluedHashMap;
-import org.keycloak.storage.StorageProviderModel;
 
-import java.io.Serializable;
 import java.security.PrivateKey;
 import java.security.PublicKey;
-import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -111,7 +106,8 @@ public class CachedRealm extends AbstractRevisioned {
 
     protected List<RequiredCredentialModel> requiredCredentials;
     protected List<UserFederationProviderModel> userFederationProviders;
-    protected List<StorageProviderModel> storageProviders;
+    protected MultivaluedHashMap<String, ComponentModel> componentsByParent = new MultivaluedHashMap<>();
+    protected Map<String, ComponentModel> components = new HashMap<>();
     protected MultivaluedHashMap<String, UserFederationMapperModel> userFederationMappers = new MultivaluedHashMap<String, UserFederationMapperModel>();
     protected Set<UserFederationMapperModel> userFederationMapperSet;
     protected List<IdentityProviderModel> identityProviders;
@@ -208,7 +204,6 @@ public class CachedRealm extends AbstractRevisioned {
 
         requiredCredentials = model.getRequiredCredentials();
         userFederationProviders = model.getUserFederationProviders();
-        storageProviders = model.getStorageProviders();
         userFederationMapperSet = model.getUserFederationMappers();
         for (UserFederationMapperModel mapper : userFederationMapperSet) {
             this.userFederationMappers.add(mapper.getFederationProviderId(), mapper);
@@ -279,6 +274,13 @@ public class CachedRealm extends AbstractRevisioned {
         resetCredentialsFlow = model.getResetCredentialsFlow();
         clientAuthenticationFlow = model.getClientAuthenticationFlow();
 
+        for (ComponentModel component : model.getComponents()) {
+            componentsByParent.add(component.getParentId() + component.getProviderType(), component);
+        }
+        for (ComponentModel component : model.getComponents()) {
+            components.put(component.getId(), component);
+        }
+
     }
 
     protected void cacheClientTemplates(RealmModel model) {
@@ -602,7 +604,11 @@ public class CachedRealm extends AbstractRevisioned {
         return requiredActionProviderList;
     }
 
-    public List<StorageProviderModel> getStorageProviders() {
-        return storageProviders;
+    public MultivaluedHashMap<String, ComponentModel> getComponentsByParent() {
+        return componentsByParent;
+    }
+
+    public Map<String, ComponentModel> getComponents() {
+        return components;
     }
 }
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 69f0510..ffaf469 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
@@ -19,11 +19,10 @@ package org.keycloak.models.cache.infinispan;
 
 import org.keycloak.Config;
 import org.keycloak.common.enums.SslRequired;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.*;
-import org.keycloak.models.cache.CacheRealmProvider;
 import org.keycloak.models.cache.infinispan.entities.CachedRealm;
 import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.storage.StorageProviderModel;
 
 import java.security.Key;
 import java.security.PrivateKey;
@@ -747,48 +746,6 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
-    public StorageProviderModel addStorageProvider(StorageProviderModel provider) {
-        getDelegateForUpdate();
-        return updated.addStorageProvider(provider);
-    }
-
-    @Override
-    public void updateStorageProvider(StorageProviderModel provider) {
-        getDelegateForUpdate();
-        updated.updateStorageProvider(provider);
-
-    }
-
-    @Override
-    public void removeStorageProvider(StorageProviderModel provider) {
-        getDelegateForUpdate();
-        updated.removeStorageProvider(provider);
-
-    }
-
-    @Override
-    public void setStorageProviders(List<StorageProviderModel> providers) {
-        getDelegateForUpdate();
-        updated.setStorageProviders(providers);
-
-    }
-
-    @Override
-    public List<StorageProviderModel> getStorageProviders() {
-        if (isUpdated()) return updated.getStorageProviders();
-        return cached.getStorageProviders();
-    }
-
-    @Override
-    public StorageProviderModel getStorageProvider(String id) {
-        if (isUpdated()) return updated.getStorageProvider(id);
-        for (StorageProviderModel model : cached.getStorageProviders()) {
-            if (model.getId().equals(id)) return model;
-        }
-        return null;
-    }
-
-    @Override
     public String getLoginTheme() {
         if (isUpdated()) return updated.getLoginTheme();
         return cached.getLoginTheme();
@@ -1451,6 +1408,52 @@ public class RealmAdapter implements RealmModel {
         return cacheSession.getClientTemplateById(id, this);
     }
 
+    @Override
+    public ComponentModel addComponentModel(ComponentModel model) {
+        getDelegateForUpdate();
+        return updated.addComponentModel(model);
+    }
+
+    @Override
+    public void updateComponent(ComponentModel component) {
+        getDelegateForUpdate();
+        updated.updateComponent(component);
+
+    }
+
+    @Override
+    public void removeComponent(ComponentModel component) {
+        getDelegateForUpdate();
+        updated.removeComponent(component);
+
+    }
+
+    @Override
+    public void removeComponents(String parentId) {
+        getDelegateForUpdate();
+        updated.removeComponents(parentId);
 
+    }
 
+    @Override
+    public List<ComponentModel> getComponents(String parentId, String providerType) {
+        if (isUpdated()) return updated.getComponents(parentId, providerType);
+        List<ComponentModel> components = cached.getComponentsByParent().getList(parentId + providerType);
+        if (components == null) return Collections.EMPTY_LIST;
+        return Collections.unmodifiableList(components);
+    }
+
+    @Override
+    public List<ComponentModel> getComponents() {
+        if (isUpdated()) return updated.getComponents();
+        List<ComponentModel> results = new LinkedList<>();
+        results.addAll(cached.getComponents().values());
+         return Collections.unmodifiableList(results);
+    }
+
+    @Override
+    public ComponentModel getComponent(String id) {
+        if (isUpdated()) return updated.getComponent(id);
+        return cached.getComponents().get(id);
+    }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
index 2a3880d..7a72964 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
@@ -132,8 +132,8 @@ public class RealmCacheSession implements CacheRealmProvider {
         this.cache = cache;
         this.session = session;
         this.startupRevision = cache.getCurrentCounter();
-        session.getTransaction().enlistPrepare(getPrepareTransaction());
-        session.getTransaction().enlistAfterCompletion(getAfterTransaction());
+        session.getTransactionManager().enlistPrepare(getPrepareTransaction());
+        session.getTransactionManager().enlistAfterCompletion(getAfterTransaction());
     }
 
     public long getStartupRevision() {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
index c5e4c30..3016ae8 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
@@ -19,6 +19,7 @@ package org.keycloak.models.cache.infinispan;
 
 import org.jboss.logging.Logger;
 import org.keycloak.common.constants.ServiceAccountConstants;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.FederatedIdentityModel;
@@ -39,7 +40,6 @@ import org.keycloak.models.cache.infinispan.entities.CachedUser;
 import org.keycloak.models.cache.infinispan.entities.CachedUserConsent;
 import org.keycloak.models.cache.infinispan.entities.CachedUserConsents;
 import org.keycloak.models.cache.infinispan.entities.UserListQuery;
-import org.keycloak.storage.StorageProviderModel;
 
 import java.util.*;
 
@@ -65,7 +65,7 @@ public class UserCacheSession implements CacheUserProvider {
         this.cache = cache;
         this.session = session;
         this.startupRevision = cache.getCurrentCounter();
-        session.getTransaction().enlistAfterCompletion(getTransaction());
+        session.getTransactionManager().enlistAfterCompletion(getTransaction());
     }
 
     @Override
@@ -665,7 +665,8 @@ public class UserCacheSession implements CacheUserProvider {
     }
 
     @Override
-    public void preRemove(RealmModel realm, StorageProviderModel provider) {
-        getDelegate().preRemove(realm, provider);
+    public void preRemove(RealmModel realm, ComponentModel component) {
+        getDelegate().preRemove(realm, component);
+
     }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 960ea25..614fa9f 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -81,7 +81,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         this.loginFailureCache = loginFailureCache;
         this.tx = new InfinispanKeycloakTransaction();
 
-        session.getTransaction().enlistAfterCompletion(tx);
+        session.getTransactionManager().enlistAfterCompletion(tx);
     }
 
     protected Cache<String, SessionEntity> getCache(boolean offline) {
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
index 431a638..4272fb8 100755
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
@@ -63,7 +63,7 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
 
         EntityManager em = emf.createEntityManager();
         em = PersistenceExceptionConverter.create(em);
-        session.getTransaction().enlist(new JpaKeycloakTransaction(em));
+        session.getTransactionManager().enlist(new JpaKeycloakTransaction(em));
         return new DefaultJpaConnectionProvider(em);
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ComponentConfigEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ComponentConfigEntity.java
new file mode 100755
index 0000000..bf141a8
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ComponentConfigEntity.java
@@ -0,0 +1,110 @@
+/*
+ * 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.models.jpa.entities;
+
+import javax.persistence.Access;
+import javax.persistence.AccessType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+        @NamedQuery(name="getComponentConfig", query="select attr from ComponentConfigEntity attr where attr.component = :component"),
+        @NamedQuery(name="deleteComponentConfigByComponent", query="delete from  ComponentConfigEntity attr where attr.component = :component"),
+        @NamedQuery(name="deleteComponentConfigByRealm", query="delete from  ComponentConfigEntity attr where attr.component IN (select u from ComponentEntity u where u.realm=:realm)"),
+        @NamedQuery(name="deleteComponentConfigByParent", query="delete from  ComponentConfigEntity attr where attr.component IN (select u from ComponentEntity u where u.parentId=:parentId)"),
+})
+@Table(name="COMPONENT_CONFIG")
+@Entity
+public class ComponentConfigEntity {
+
+    @Id
+    @Column(name="ID", length = 36)
+    @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity.  This avoids an extra SQL
+    protected String id;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name = "COMPONENT_ID")
+    protected ComponentEntity component;
+
+    @Column(name = "NAME")
+    protected String name;
+    @Column(name = "VALUE")
+    protected String value;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public ComponentEntity getComponent() {
+        return component;
+    }
+
+    public void setComponent(ComponentEntity component) {
+        this.component = component;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null) return false;
+        if (!(o instanceof ComponentConfigEntity)) return false;
+
+        ComponentConfigEntity that = (ComponentConfigEntity) o;
+
+        if (!id.equals(that.getId())) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index 3c9ae5f..e45d819 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -145,9 +145,6 @@ public class RealmEntity {
     List<UserFederationProviderEntity> userFederationProviders = new ArrayList<>();
 
     @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
-    List<StorageProviderEntity> storageProviders = new ArrayList<>();
-
-    @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
     Collection<UserFederationMapperEntity> userFederationMappers = new ArrayList<UserFederationMapperEntity>();
 
     @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
@@ -554,14 +551,6 @@ public class RealmEntity {
         this.userFederationProviders = userFederationProviders;
     }
 
-    public List<StorageProviderEntity> getStorageProviders() {
-        return storageProviders;
-    }
-
-    public void setStorageProviders(List<StorageProviderEntity> storageProviders) {
-        this.storageProviders = storageProviders;
-    }
-
     public Collection<UserFederationMapperEntity> getUserFederationMappers() {
         return userFederationMappers;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index 2cf74ad..f5d2666 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -34,7 +34,6 @@ import org.keycloak.models.jpa.entities.GroupEntity;
 import org.keycloak.models.jpa.entities.RealmEntity;
 import org.keycloak.models.jpa.entities.RoleEntity;
 import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.storage.StorageProviderModel;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
@@ -128,9 +127,6 @@ public class JpaRealmProvider implements RealmProvider {
         em.refresh(realm);
         final RealmAdapter adapter = new RealmAdapter(session, em, realm);
         session.users().preRemove(adapter);
-        for (StorageProviderModel provider : adapter.getStorageProviders()) {
-            adapter.removeStorageProvider(provider);
-        }
 
         realm.getDefaultGroups().clear();
         em.flush();
@@ -141,6 +137,10 @@ public class JpaRealmProvider implements RealmProvider {
                 .setParameter("realm", realm).executeUpdate();
         num = em.createNamedQuery("deleteGroupsByRealm")
                 .setParameter("realm", realm).executeUpdate();
+        num = em.createNamedQuery("deleteComponentConfigByRealm")
+                .setParameter("realm", realm).executeUpdate();
+        num = em.createNamedQuery("deleteComponentByRealm")
+                .setParameter("realm", realm).executeUpdate();
 
         TypedQuery<String> query = em.createNamedQuery("getClientIdsByRealm", String.class);
         query.setParameter("realm", realm.getId());
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index 02403ee..a928e58 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.models.jpa;
 
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.FederatedIdentityModel;
@@ -42,7 +43,6 @@ import org.keycloak.models.jpa.entities.UserEntity;
 import org.keycloak.models.utils.CredentialValidation;
 import org.keycloak.models.utils.DefaultRoles;
 import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.storage.StorageProviderModel;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
@@ -712,7 +712,7 @@ public class JpaUserProvider implements UserProvider {
     }
 
     @Override
-    public void preRemove(RealmModel realm, StorageProviderModel link) {
+    public void preRemove(RealmModel realm, ComponentModel component) {
 
     }
 }
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 d6837aa..093ebf3 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
@@ -18,7 +18,8 @@
 package org.keycloak.models.jpa;
 
 import org.jboss.logging.Logger;
-import org.keycloak.connections.jpa.util.JpaUtils;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.common.enums.SslRequired;
 import org.keycloak.jose.jwk.JWKBuilder;
 import org.keycloak.models.AuthenticationExecutionModel;
@@ -43,8 +44,6 @@ import org.keycloak.models.UserFederationProviderCreationEventImpl;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.jpa.entities.*;
 import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.storage.StorageProvider;
-import org.keycloak.storage.StorageProviderModel;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
@@ -1004,179 +1003,6 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
     }
 
     @Override
-    public StorageProviderModel getStorageProvider(String id) {
-        StorageProviderEntity entity = em.find(StorageProviderEntity.class, id);
-        if (entity == null) return null;
-        return toModel(entity);
-    }
-
-    @Override
-    public List<StorageProviderModel> getStorageProviders() {
-        List<StorageProviderEntity> entities = realm.getStorageProviders();
-        if (entities.isEmpty()) return Collections.EMPTY_LIST;
-        List<StorageProviderEntity> copy = new LinkedList<>();
-        for (StorageProviderEntity entity : entities) {
-            copy.add(entity);
-
-        }
-        List<StorageProviderModel> result = new LinkedList<>();
-        for (StorageProviderEntity entity : copy) {
-            result.add(toModel(entity));
-        }
-        Collections.sort(result, StorageProviderModel.comparator);
-
-        return Collections.unmodifiableList(result);
-    }
-
-    protected StorageProviderModel toModel(StorageProviderEntity entity) {
-        StorageProviderModel model = new StorageProviderModel();
-        model.setId(entity.getId());
-        model.setProviderName(entity.getProviderName());
-        model.getConfig().putAll(entity.getConfig());
-        model.setPriority(entity.getPriority());
-        model.setDisplayName(entity.getDisplayName());
-        return model;
-    }
-
-    @Override
-    public StorageProviderModel addStorageProvider(StorageProviderModel model) {
-        KeycloakModelUtils.ensureUniqueDisplayName(model.getDisplayName(), null, getStorageProviders());
-
-        String id = KeycloakModelUtils.generateId();
-        StorageProviderEntity entity = new StorageProviderEntity();
-        entity.setId(id);
-        entity.setRealm(realm);
-        entity.setProviderName(model.getProviderName());
-        entity.setConfig(model.getConfig());
-        entity.setPriority(model.getPriority());
-        String displayName = model.getDisplayName();
-        if (model.getDisplayName() == null) {
-            displayName = id;
-        }
-        entity.setDisplayName(displayName);
-        em.persist(entity);
-        realm.getStorageProviders().add(entity);
-        em.flush();
-        StorageProviderModel providerModel = toModel(entity);
-
-        return providerModel;
-    }
-
-    @Override
-    public void removeStorageProvider(StorageProviderModel provider) {
-        Iterator<StorageProviderEntity> it = realm.getStorageProviders().iterator();
-        while (it.hasNext()) {
-            StorageProviderEntity entity = it.next();
-            if (entity.getId().equals(provider.getId())) {
-
-                session.users().preRemove(this, provider);
-
-                it.remove();
-                em.remove(entity);
-                return;
-            }
-        }
-    }
-    @Override
-    public void updateStorageProvider(StorageProviderModel model) {
-        KeycloakModelUtils.ensureUniqueDisplayName(model.getDisplayName(), model, getStorageProviders());
-
-        Iterator<StorageProviderEntity> it = realm.getStorageProviders().iterator();
-        while (it.hasNext()) {
-            StorageProviderEntity entity = it.next();
-            if (entity.getId().equals(model.getId())) {
-                String displayName = model.getDisplayName();
-                if (displayName != null) {
-                    entity.setDisplayName(model.getDisplayName());
-                }
-                entity.setConfig(model.getConfig());
-                entity.setPriority(model.getPriority());
-                entity.setProviderName(model.getProviderName());
-                entity.setPriority(model.getPriority());
-                break;
-            }
-        }
-    }
-
-    @Override
-    public void setStorageProviders(List<StorageProviderModel> providers) {
-        for (StorageProviderModel currentProvider : providers) {
-            KeycloakModelUtils.ensureUniqueDisplayName(currentProvider.getDisplayName(), currentProvider, providers);
-        }
-
-        Iterator<StorageProviderEntity> it = realm.getStorageProviders().iterator();
-        while (it.hasNext()) {
-            StorageProviderEntity entity = it.next();
-            boolean found = false;
-            for (StorageProviderModel model : providers) {
-                if (entity.getId().equals(model.getId())) {
-                    entity.setConfig(model.getConfig());
-                    entity.setPriority(model.getPriority());
-                    entity.setProviderName(model.getProviderName());
-                    String displayName = model.getDisplayName();
-                    if (displayName != null) {
-                        entity.setDisplayName(displayName);
-                    }
-                    found = true;
-                    break;
-                }
-
-            }
-            if (found) continue;
-            session.users().preRemove(this, toModel(entity));
-            removeFederationMappersForProvider(entity.getId());
-
-            it.remove();
-            em.remove(entity);
-        }
-
-        List<StorageProviderModel> add = new LinkedList<>();
-        for (StorageProviderModel model : providers) {
-            boolean found = false;
-            for (StorageProviderEntity entity : realm.getStorageProviders()) {
-                if (entity.getId().equals(model.getId())) {
-                    found = true;
-                    break;
-                }
-            }
-            if (!found) add.add(model);
-        }
-
-        for (StorageProviderModel model : add) {
-            StorageProviderEntity entity = new StorageProviderEntity();
-            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());
-            entity.setPriority(model.getPriority());
-            String displayName = model.getDisplayName();
-            if (displayName == null) {
-                displayName = entity.getId();
-            }
-            entity.setDisplayName(displayName);
-            entity.setRealm(realm);
-            em.persist(entity);
-            realm.getStorageProviders().add(entity);
-
-        }
-    }
-
-    protected StorageProviderEntity getStorageProviderEntityById(String id) {
-        for (StorageProviderEntity entity : realm.getStorageProviders()) {
-            if (entity.getId().equals(id)) {
-                return entity;
-            }
-        }
-        return null;
-    }
-
-    @Override
     public RoleModel getRole(String name) {
         return session.realms().getRealmRole(this, name);
     }
@@ -2280,4 +2106,131 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
         return session.realms().getClientTemplateById(id, this);
     }
 
+    @Override
+    public ComponentModel addComponentModel(ComponentModel model) {
+        ComponentEntity c = new ComponentEntity();
+        if (model.getId() == null) {
+            c.setId(KeycloakModelUtils.generateId());
+        } else {
+            c.setId(model.getId());
+        }
+        c.setName(model.getName());
+        c.setParentId(model.getParentId());
+        c.setProviderType(model.getProviderType());
+        c.setProviderId(model.getProviderId());
+        c.setRealm(realm);
+        em.persist(c);
+        setConfig(model, c);
+        model.setId(c.getId());
+        return model;
+    }
+
+    protected void setConfig(ComponentModel model, ComponentEntity c) {
+        for (String key : model.getConfig().keySet()) {
+            List<String> vals = model.getConfig().get(key);
+            for (String val : vals) {
+                ComponentConfigEntity config = new ComponentConfigEntity();
+                config.setId(KeycloakModelUtils.generateId());
+                config.setName(key);
+                config.setValue(val);
+                config.setComponent(c);
+                em.persist(config);
+            }
+        }
+    }
+
+    @Override
+    public void updateComponent(ComponentModel component) {
+        ComponentEntity c = em.find(ComponentEntity.class, component.getId());
+        if (c == null) return;
+        c.setName(component.getName());
+        c.setProviderId(component.getProviderId());
+        c.setProviderType(component.getProviderType());
+        c.setParentId(component.getParentId());
+        em.createNamedQuery("deleteComponentConfigByComponent").setParameter("component", c).executeUpdate();
+        em.flush();
+        setConfig(component, c);
+
+
+    }
+
+    @Override
+    public void removeComponent(ComponentModel component) {
+        ComponentEntity c = em.find(ComponentEntity.class, component.getId());
+        if (c == null) return;
+        session.users().preRemove(this, component);
+        em.createNamedQuery("deleteComponentConfigByComponent").setParameter("component", c).executeUpdate();
+        em.remove(c);
+    }
+
+    @Override
+    public void removeComponents(String parentId) {
+        TypedQuery<String> query = em.createNamedQuery("getComponentIdsByParent", String.class)
+                .setParameter("realm", realm)
+                .setParameter("parentId", parentId);
+        List<String> results = query.getResultList();
+        if (results.isEmpty()) return;
+        for (String id : results) {
+            session.users().preRemove(this, getComponent(id));
+        }
+        em.createNamedQuery("deleteComponentConfigByParent").setParameter("parentId", parentId).executeUpdate();
+        em.createNamedQuery("deleteComponentByParent").setParameter("parentId", parentId).executeUpdate();
+
+    }
+
+    @Override
+    public List<ComponentModel> getComponents(String parentId, String providerType) {
+        if (parentId == null) parentId = getId();
+        TypedQuery<ComponentEntity> query = em.createNamedQuery("getComponentsByParentAndType", ComponentEntity.class)
+                .setParameter("realm", realm)
+                .setParameter("parentId", parentId)
+                .setParameter("providerType", providerType);
+        List<ComponentEntity> results = query.getResultList();
+        List<ComponentModel> rtn = new LinkedList<>();
+        for (ComponentEntity c : results) {
+            ComponentModel model = entityToModel(c);
+            rtn.add(model);
+
+        }
+        return rtn;
+    }
+
+    protected ComponentModel entityToModel(ComponentEntity c) {
+        ComponentModel model = new ComponentModel();
+        model.setId(c.getId());
+        model.setName(c.getName());
+        model.setProviderType(c.getProviderType());
+        model.setProviderId(c.getProviderId());
+        model.setParentId(c.getParentId());
+        MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
+        TypedQuery<ComponentConfigEntity> configQuery = em.createNamedQuery("getComponentConfig", ComponentConfigEntity.class)
+                .setParameter("component", c);
+        List<ComponentConfigEntity> configResults = configQuery.getResultList();
+        for (ComponentConfigEntity configEntity : configResults) {
+            config.add(configEntity.getName(), configEntity.getValue());
+        }
+        model.setConfig(config);
+        return model;
+    }
+
+    @Override
+    public List<ComponentModel> getComponents() {
+        TypedQuery<ComponentEntity> query = em.createNamedQuery("getComponents", ComponentEntity.class)
+                .setParameter("realm", realm);
+        List<ComponentEntity> results = query.getResultList();
+        List<ComponentModel> rtn = new LinkedList<>();
+        for (ComponentEntity c : results) {
+            ComponentModel model = entityToModel(c);
+            rtn.add(model);
+
+        }
+        return rtn;
+    }
+
+    @Override
+    public ComponentModel getComponent(String id) {
+        ComponentEntity c = em.find(ComponentEntity.class, id);
+        if (c == null) return null;
+        return entityToModel(c);
+    }
 }
\ No newline at end of file
diff --git a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
index d64a3ea..f6710dd 100644
--- a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
@@ -17,6 +17,7 @@
 package org.keycloak.storage.jpa;
 
 import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.GroupModel;
@@ -31,11 +32,10 @@ import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.jpa.entities.CredentialEntity;
 import org.keycloak.models.utils.FederatedCredentials;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.storage.StorageId;
-import org.keycloak.storage.StorageProviderModel;
+import org.keycloak.storage.UserStorageProvider;
 import org.keycloak.storage.federated.UserAttributeFederatedStorage;
 import org.keycloak.storage.federated.UserBrokerLinkFederatedStorage;
 import org.keycloak.storage.federated.UserConsentFederatedStorage;
@@ -719,7 +719,9 @@ public class JpaUserFederatedStorageProvider implements
     }
 
     @Override
-    public void preRemove(RealmModel realm, StorageProviderModel model) {
+    public void preRemove(RealmModel realm, ComponentModel model) {
+        if (!model.getProviderType().equals(UserStorageProvider.class.getName())) return;
+
         em.createNamedQuery("deleteBrokerLinkByStorageProvider")
                 .setParameter("storageProviderId", model.getId())
                 .executeUpdate();
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.1.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.1.0.xml
index 988662d..9b01ace 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.1.0.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.1.0.xml
@@ -149,22 +149,26 @@
             <column name="STORAGE_PROVIDER_ID" type="VARCHAR(36)"/>
         </createTable>
 
-        <createTable tableName="STORAGE_PROVIDER_CONFIG">
-            <column name="STORAGE_PROVIDER_ID" type="VARCHAR(36)">
+        <createTable tableName="COMPONENT_CONFIG">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="COMPONENT_ID" type="VARCHAR(36)">
                 <constraints nullable="false"/>
             </column>
-            <column name="VALUE" type="VARCHAR(255)"/>
             <column name="NAME" type="VARCHAR(255)">
                 <constraints nullable="false"/>
             </column>
+            <column name="VALUE" type="VARCHAR(4096)"/>
         </createTable>
-        <createTable tableName="STORAGE_PROVIDER">
+        <createTable tableName="COMPONENT">
             <column name="ID" type="VARCHAR(36)">
                 <constraints nullable="false"/>
             </column>
-            <column name="DISPLAY_NAME" type="VARCHAR(255)"/>
-            <column name="PRIORITY" type="INT"/>
-            <column name="PROVIDER_NAME" type="VARCHAR(255)"/>
+            <column name="NAME" type="VARCHAR(255)"/>
+            <column name="PARENT_ID" type="VARCHAR(36)"/>
+            <column name="PROVIDER_ID" type="VARCHAR(36)"/>
+            <column name="PROVIDER_TYPE" type="VARCHAR(255)"/>
             <column name="REALM_ID" type="VARCHAR(36)"/>
         </createTable>
 
@@ -185,10 +189,10 @@
         <addPrimaryKey columnNames="ROLE_ID, USER_ID" constraintName="CONSTR_FED_USER_ROLE" tableName="FED_USER_ROLE_MAPPING"/>
         <addPrimaryKey columnNames="REQUIRED_ACTION, USER_ID" constraintName="CONSTR_FED_REQUIRED_ACTION" tableName="FED_USER_REQUIRED_ACTION"/>
 
-        <addPrimaryKey columnNames="ID" constraintName="CONSTR_STORAGE_PROVIDER_PK" tableName="STORAGE_PROVIDER"/>
-        <addPrimaryKey columnNames="STORAGE_PROVIDER_ID, NAME" constraintName="CONSTR_STORAGE_CONFIG" tableName="STORAGE_PROVIDER_CONFIG"/>
-        <!--
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="STORAGE_PROVIDER" constraintName="FK_STORAGE_PROVIDER_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
-        -->
+        <addPrimaryKey columnNames="ID" constraintName="CONSTR_COMPONENT_PK" tableName="COMPONENT"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTR_COMPONENT_CONFIG_PK" tableName="COMPONENT_CONFIG"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="COMPONENT" constraintName="FK_COMPONENT_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="COMPONENT_ID" baseTableName="COMPONENT_CONFIG" constraintName="FK_COMPONENT_CONFIG" referencedColumnNames="ID" referencedTableName="COMPONENT"/>
+
     </changeSet>
 </databaseChangeLog>
\ No newline at end of file
diff --git a/model/jpa/src/main/resources/META-INF/persistence.xml b/model/jpa/src/main/resources/META-INF/persistence.xml
index d912033..0b2ff23 100755
--- a/model/jpa/src/main/resources/META-INF/persistence.xml
+++ b/model/jpa/src/main/resources/META-INF/persistence.xml
@@ -25,7 +25,8 @@
         <class>org.keycloak.models.jpa.entities.RealmEntity</class>
         <class>org.keycloak.models.jpa.entities.RealmAttributeEntity</class>
         <class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
-        <class>org.keycloak.models.jpa.entities.StorageProviderEntity</class>
+        <class>org.keycloak.models.jpa.entities.ComponentConfigEntity</class>
+        <class>org.keycloak.models.jpa.entities.ComponentEntity</class>
         <class>org.keycloak.models.jpa.entities.UserFederationProviderEntity</class>
         <class>org.keycloak.models.jpa.entities.UserFederationMapperEntity</class>
         <class>org.keycloak.models.jpa.entities.RoleEntity</class>
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index d8d87f5..440886f 100755
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -22,7 +22,6 @@ import java.net.UnknownHostException;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 import javax.net.ssl.SSLSocketFactory;
 
@@ -79,6 +78,7 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
             "org.keycloak.models.entities.RequiredActionProviderEntity",
             "org.keycloak.models.entities.PersistentUserSessionEntity",
             "org.keycloak.models.entities.PersistentClientSessionEntity",
+            "org.keycloak.models.entities.StorageProviderEntity",
             "org.keycloak.authorization.mongo.entities.PolicyEntity",
             "org.keycloak.authorization.mongo.entities.ResourceEntity",
             "org.keycloak.authorization.mongo.entities.ResourceServerEntity",
@@ -141,7 +141,7 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
         lazyInit(session);
 
         TransactionMongoStoreInvocationContext invocationContext = new TransactionMongoStoreInvocationContext(mongoStore);
-        session.getTransaction().enlist(new MongoKeycloakTransaction(invocationContext));
+        session.getTransactionManager().enlist(new MongoKeycloakTransaction(invocationContext));
         return new DefaultMongoConnectionProvider(db, mongoStore, invocationContext);
     }
 
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index ad811d4..96d1f91 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -21,6 +21,7 @@ import com.mongodb.BasicDBObject;
 import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 
+import org.keycloak.component.ComponentModel;
 import org.keycloak.connections.mongo.api.MongoStore;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.ClientModel;
@@ -44,7 +45,6 @@ import org.keycloak.models.entities.UserConsentEntity;
 import org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity;
 import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
 import org.keycloak.models.utils.CredentialValidation;
-import org.keycloak.storage.StorageProviderModel;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -632,7 +632,7 @@ public class MongoUserProvider implements UserProvider {
     }
 
     @Override
-    public void preRemove(RealmModel realm, StorageProviderModel link) {
+    public void preRemove(RealmModel realm, ComponentModel component) {
 
     }
 }
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 cb6bba7..8b945ef 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
@@ -20,6 +20,8 @@ package org.keycloak.models.mongo.keycloak.adapters;
 import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.common.enums.SslRequired;
 import org.keycloak.jose.jwk.JWKBuilder;
@@ -47,20 +49,18 @@ import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.entities.AuthenticationExecutionEntity;
 import org.keycloak.models.entities.AuthenticationFlowEntity;
 import org.keycloak.models.entities.AuthenticatorConfigEntity;
+import org.keycloak.models.entities.ComponentEntity;
 import org.keycloak.models.entities.IdentityProviderEntity;
 import org.keycloak.models.entities.IdentityProviderMapperEntity;
 import org.keycloak.models.entities.RequiredActionProviderEntity;
 import org.keycloak.models.entities.RequiredCredentialEntity;
-import org.keycloak.models.entities.StorageProviderEntity;
 import org.keycloak.models.entities.UserFederationMapperEntity;
 import org.keycloak.models.entities.UserFederationProviderEntity;
 import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
 import org.keycloak.models.mongo.keycloak.entities.MongoClientTemplateEntity;
-import org.keycloak.models.mongo.keycloak.entities.MongoGroupEntity;
 import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
 import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
 import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.storage.StorageProviderModel;
 
 import java.security.Key;
 import java.security.PrivateKey;
@@ -1171,166 +1171,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
     }
 
     @Override
-    public StorageProviderModel addStorageProvider(StorageProviderModel model) {
-        KeycloakModelUtils.ensureUniqueDisplayName(model.getDisplayName(), null, getStorageProviders());
-
-        StorageProviderEntity entity = new StorageProviderEntity();
-        entity.setId(KeycloakModelUtils.generateId());
-        entity.setPriority(model.getPriority());
-        entity.setProviderName(model.getProviderName());
-        entity.setConfig(model.getConfig());
-        String displayName = model.getDisplayName();
-        if (displayName == null) {
-            displayName = entity.getId();
-        }
-        entity.setDisplayName(displayName);
-        realm.getStorageProviders().add(entity);
-        updateRealm();
-
-        StorageProviderModel providerModel = new StorageProviderModel(entity.getId(), model.getProviderName(),
-                model.getConfig(), model.getPriority(), displayName);
-
-
-        return providerModel;
-    }
-
-    @Override
-    public void updateStorageProvider(StorageProviderModel provider) {
-        KeycloakModelUtils.ensureUniqueDisplayName(provider.getDisplayName(), provider, getStorageProviders());
-
-        Iterator<StorageProviderEntity> it = realm.getStorageProviders().iterator();
-        while (it.hasNext()) {
-            StorageProviderEntity entity = it.next();
-            if (entity.getId().equals(provider.getId())) {
-                entity.setProviderName(provider.getProviderName());
-                entity.setConfig(provider.getConfig());
-                entity.setPriority(provider.getPriority());
-                String displayName = provider.getDisplayName();
-                if (displayName != null) {
-                    entity.setDisplayName(provider.getDisplayName());
-                }
-            }
-        }
-        updateRealm();
-
-    }
-
-    @Override
-    public void removeStorageProvider(StorageProviderModel provider) {
-        Iterator<StorageProviderEntity> it = realm.getStorageProviders().iterator();
-        while (it.hasNext()) {
-            StorageProviderEntity entity = it.next();
-            if (entity.getId().equals(provider.getId())) {
-                session.users().preRemove(this, new StorageProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName()
-                        ));
-
-                it.remove();
-            }
-        }
-        updateRealm();
-
-    }
-
-    @Override
-    public void setStorageProviders(List<StorageProviderModel> providers) {
-        for (StorageProviderModel currentProvider : providers) {
-            KeycloakModelUtils.ensureUniqueDisplayName(currentProvider.getDisplayName(), currentProvider, providers);
-        }
-
-        List<StorageProviderEntity> existingProviders = realm.getStorageProviders();
-        List<StorageProviderEntity> toRemove = new LinkedList<>();
-        for (StorageProviderEntity entity : existingProviders) {
-            boolean found = false;
-            for (StorageProviderModel model : providers) {
-                if (entity.getId().equals(model.getId())) {
-                    entity.setConfig(model.getConfig());
-                    entity.setPriority(model.getPriority());
-                    entity.setProviderName(model.getProviderName());
-                    String displayName = model.getDisplayName();
-                    if (displayName != null) {
-                        entity.setDisplayName(displayName);
-                    }
-                    found = true;
-                    break;
-                }
-
-            }
-            if (found) continue;
-            session.users().preRemove(this, new StorageProviderModel(entity.getId(), entity.getProviderName(),
-                    entity.getConfig(), entity.getPriority(), entity.getDisplayName()));
-            toRemove.add(entity);
-        }
-
-        for (StorageProviderEntity entity : toRemove) {
-            realm.getStorageProviders().remove(entity);
-        }
-
-        List<StorageProviderModel> add = new LinkedList<>();
-        for (StorageProviderModel model : providers) {
-            boolean found = false;
-            for (StorageProviderEntity entity : realm.getStorageProviders()) {
-                if (entity.getId().equals(model.getId())) {
-                    found = true;
-                    break;
-                }
-            }
-            if (!found) add.add(model);
-        }
-
-        for (StorageProviderModel model : add) {
-            StorageProviderEntity entity = new StorageProviderEntity();
-            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());
-            String displayName = model.getDisplayName();
-            if (displayName == null) {
-                displayName = entity.getId();
-            }
-            entity.setDisplayName(displayName);
-            realm.getStorageProviders().add(entity);
-
-        }
-
-        updateRealm();
-
-    }
-
-    @Override
-    public List<StorageProviderModel> getStorageProviders() {
-        List<StorageProviderEntity> entities = realm.getStorageProviders();
-        if (entities.isEmpty()) return Collections.EMPTY_LIST;
-        List<StorageProviderEntity> copy = new LinkedList<>();
-        for (StorageProviderEntity entity : entities) {
-            copy.add(entity);
-
-        }
-        List<StorageProviderModel> result = new LinkedList<>();
-        for (StorageProviderEntity entity : copy) {
-            result.add(new StorageProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName()
-                    ));
-        }
-
-        Collections.sort(result, StorageProviderModel.comparator);
-        return Collections.unmodifiableList(result);
-    }
-
-    @Override
-    public StorageProviderModel getStorageProvider(String id) {
-        for (StorageProviderEntity entity : realm.getStorageProviders()) {
-            if (entity.getId().equals(id)) return new StorageProviderModel(entity.getId(), entity.getProviderName(),
-                    entity.getConfig(), entity.getPriority(), entity.getDisplayName());
-        }
-        return null;
-    }
-
-    @Override
     public boolean isEventsEnabled() {
         return realm.isEventsEnabled();
     }
@@ -2217,5 +2057,115 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         return model.getClientTemplateById(id, this);
     }
 
+    @Override
+    public ComponentModel addComponentModel(ComponentModel model) {
+        ComponentEntity entity = new ComponentEntity();
+        if (model.getId() == null) {
+            entity.setId(KeycloakModelUtils.generateId());
+        } else {
+            entity.setId(model.getId());
+        }
+        entity.setConfig(model.getConfig());
+        entity.setId(model.getId());
+        entity.setParentId(model.getParentId());
+        entity.setProviderType(model.getProviderType());
+        entity.setProviderId(model.getProviderId());
+        entity.setName(model.getName());
+        model.setId(entity.getId());
+        realm.getComponentEntities().add(entity);
+        updateRealm();
+
+        return model;
+    }
+
+    @Override
+    public void updateComponent(ComponentModel model) {
+        for (ComponentEntity entity : realm.getComponentEntities()) {
+            if (entity.getId().equals(model.getId())) {
+                entity.setConfig(model.getConfig());
+                entity.setId(model.getId());
+                entity.setParentId(model.getParentId());
+                entity.setProviderType(model.getProviderType());
+                entity.setProviderId(model.getProviderId());
+                entity.setName(model.getName());
+
+            }
+        }
+        updateRealm();
+
+    }
+
+    @Override
+    public void removeComponent(ComponentModel component) {
+        Iterator<ComponentEntity> it = realm.getComponentEntities().iterator();
+        while(it.hasNext()) {
+            if (it.next().getId().equals(component.getId())) {
+                session.users().preRemove(this, component);
+                it.remove();
+                break;
+            }
+        }
+        updateRealm();
+
+    }
+
+    @Override
+    public void removeComponents(String parentId) {
+        Iterator<ComponentEntity> it = realm.getComponentEntities().iterator();
+        while(it.hasNext()) {
+            ComponentEntity next = it.next();
+            if (next.getParentId().equals(parentId)) {
+                session.users().preRemove(this, entityToModel(next));
+                it.remove();
+            }
+        }
+        updateRealm();
+
+    }
+
+    @Override
+    public List<ComponentModel> getComponents(String parentId, String providerType) {
+        List<ComponentModel> results = new LinkedList<>();
+        for (ComponentEntity entity : realm.getComponentEntities()) {
+            if (entity.getParentId().equals(parentId) && entity.getProviderType().equals(providerType)) {
+                ComponentModel model = entityToModel(entity);
+                results.add(model);
+            }
+
+        }
+        return results;
+    }
 
+    protected ComponentModel entityToModel(ComponentEntity entity) {
+        ComponentModel model = new ComponentModel();
+        model.setId(entity.getId());
+        model.setName(entity.getName());
+        model.setParentId(entity.getParentId());
+        model.setProviderId(entity.getProviderId());
+        model.setProviderType(entity.getProviderType());
+        MultivaluedHashMap<String, String> map = new MultivaluedHashMap<>();
+        map.putAll(entity.getConfig());
+        model.setConfig(map);
+        return model;
+    }
+
+    @Override
+    public List<ComponentModel> getComponents() {
+        List<ComponentModel> results = new LinkedList<>();
+        for (ComponentEntity entity : realm.getComponentEntities()) {
+            ComponentModel model = entityToModel(entity);
+            results.add(model);
+        }
+        return results;
+    }
+
+    @Override
+    public ComponentModel getComponent(String id) {
+        for (ComponentEntity entity : realm.getComponentEntities()) {
+            if (entity.getId() == entity.getId()) {
+                return entityToModel(entity);
+            }
+        }
+        return null;
+    }
 }
diff --git a/server-spi/src/main/java/org/keycloak/component/ComponentModel.java b/server-spi/src/main/java/org/keycloak/component/ComponentModel.java
new file mode 100755
index 0000000..338bc4b
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/component/ComponentModel.java
@@ -0,0 +1,101 @@
+/*
+ * 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.component;
+
+import org.keycloak.common.util.MultivaluedHashMap;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Stored configuration of a User Storage provider instance.
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+public class ComponentModel implements Serializable {
+
+    private String id;
+    private String name;
+    private String providerId;
+    private String providerType;
+    private String parentId;
+    private MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
+
+    public ComponentModel() {}
+
+    public ComponentModel(ComponentModel copy) {
+        this.id = copy.id;
+        this.name = copy.name;
+        this.providerId = copy.providerId;
+        this.providerType = copy.providerType;
+        this.config = copy.config;
+    }
+
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public MultivaluedHashMap<String, String> getConfig() {
+        return config;
+    }
+
+    public void setConfig(MultivaluedHashMap<String, String> config) {
+        this.config = config;
+    }
+
+    public String getProviderId() {
+        return providerId;
+    }
+
+    public void setProviderId(String providerId) {
+        this.providerId = providerId;
+    }
+
+    public String getProviderType() {
+        return providerType;
+    }
+
+    public void setProviderType(String providerType) {
+        this.providerType = providerType;
+    }
+
+    public String getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(String parentId) {
+        this.parentId = parentId;
+    }
+}
diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java
index 8ff3664..9e7b931 100644
--- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java
+++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java
@@ -52,11 +52,9 @@ public class MigrateTo2_1_0 {
     private void migrateDefaultRequiredAction(RealmModel realm) {
         RequiredActionProviderModel otpAction = realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.CONFIGURE_TOTP.name());
 
-        if (otpAction == null) return;
-        if (!otpAction.getProviderId().equals(UserModel.RequiredAction.CONFIGURE_TOTP.name())) return;
-        if (!otpAction.getName().equals("Configure Totp")) return;
+        MigrationUtils.updateOTPRequiredAction(otpAction);
 
-        otpAction.setName("Configure OTP");
+        realm.updateRequiredActionProvider(otpAction);
     }
 
     // KEYCLOAK-3338: Changes to how role policy config is stored"
diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java
index 91f812e..08da081 100644
--- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java
+++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java
@@ -47,4 +47,12 @@ public class MigrationUtils {
         }
     }
 
+    public static void updateOTPRequiredAction(RequiredActionProviderModel otpAction) {
+        if (otpAction == null) return;
+        if (!otpAction.getProviderId().equals(UserModel.RequiredAction.CONFIGURE_TOTP.name())) return;
+        if (!otpAction.getName().equals("Configure Totp")) return;
+
+        otpAction.setName("Configure OTP");
+    }
+
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/entities/RealmEntity.java b/server-spi/src/main/java/org/keycloak/models/entities/RealmEntity.java
index 6780498..c5a6ecf 100755
--- a/server-spi/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/server-spi/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -85,7 +85,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
     private List<String> defaultGroups = new LinkedList<String>();
 
     private List<RequiredCredentialEntity> requiredCredentials = new LinkedList<>();
-    private List<StorageProviderEntity> storageProviders = new LinkedList<>();
+    private List<ComponentEntity> componentEntities = new LinkedList<>();
     private List<UserFederationProviderEntity> userFederationProviders = new LinkedList<UserFederationProviderEntity>();
     private List<UserFederationMapperEntity> userFederationMappers = new LinkedList<UserFederationMapperEntity>();
     private List<IdentityProviderEntity> identityProviders = new LinkedList<IdentityProviderEntity>();
@@ -685,12 +685,12 @@ public class RealmEntity extends AbstractIdentifiableEntity {
         this.defaultGroups = defaultGroups;
     }
 
-    public List<StorageProviderEntity> getStorageProviders() {
-        return storageProviders;
+    public List<ComponentEntity> getComponentEntities() {
+        return componentEntities;
     }
 
-    public void setStorageProviders(List<StorageProviderEntity> storageProviders) {
-        this.storageProviders = storageProviders;
+    public void setComponentEntities(List<ComponentEntity> componentEntities) {
+        this.componentEntities = componentEntities;
     }
 }
 
diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java
index 1d71b1f..8b6dcd3 100755
--- a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java
+++ b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java
@@ -32,16 +32,48 @@ public interface KeycloakSession {
 
     KeycloakContext getContext();
 
-    KeycloakTransactionManager getTransaction();
+    KeycloakTransactionManager getTransactionManager();
 
+    /**
+     * Get dedicated provider instance of provider type clazz that was created for this session.  If one hasn't been created yet,
+     * find the factory and allocate by calling ProviderFactory.create(KeycloakSession).  The provider to use is determined
+     * by the "provider" config entry in keycloak-server boot configuration. (keycloak-server.json)
+     *
+     *
+     *
+     * @param clazz
+     * @param <T>
+     * @return
+     */
     <T extends Provider> T getProvider(Class<T> clazz);
 
+    /**
+     * Get dedicated provider instance for a specific provider factory of id of provider type clazz that was created for this session.
+     * If one hasn't been created yet,
+     * find the factory and allocate by calling ProviderFactory.create(KeycloakSession).
+
+     * @param clazz
+     * @param id
+     * @param <T>
+     * @return
+     */
     <T extends Provider> T getProvider(Class<T> clazz, String id);
 
+    /**
+     * Get all provider factories that manage provider instances of class.
+     *
+     * @param clazz
+     * @param <T>
+     * @return
+     */
     <T extends Provider> Set<String> listProviderIds(Class<T> clazz);
 
     <T extends Provider> Set<T> getAllProviders(Class<T> clazz);
 
+    Object getAttribute(String attribute);
+    Object removeAttribute(String attribute);
+    void setAttribute(String name, Object value);
+
     void enlistForClose(Provider provider);
 
     KeycloakSessionFactory getKeycloakSessionFactory();
@@ -69,23 +101,41 @@ public interface KeycloakSession {
     void close();
 
     /**
-     * Possibly both cached and federated view of users depending on configuration.
+     * A cached view of all users in system.
      *
      * @return
      */
     UserFederationManager users();
 
+
+    /**
+     * Un-cached view of all users in system that does NOT include users available from the deprecated UserFederationProvider SPI.
+     *
+     * @return
+     */
     UserProvider userStorageManager();
 
     /**
-     *  Keycloak user storage.  Non-federated, but possibly cache (if it is on) view of users.
+     *  A cached view of all users in system that does NOT include users available from the deprecated UserFederationProvider SPI.
      */
     UserProvider userStorage();
 
-    UserFederatedStorageProvider userFederatedStorage();
+    /**
+     * Keycloak specific local storage for users.  No cache in front, this api talks directly to database.
+     *
+     * @return
+     */
     UserProvider userLocalStorage();
 
     /**
+     * Hybrid storage for UserStorageProviders that can't store a specific piece of keycloak data in their external storage.
+     *
+     * @return
+     */
+    UserFederatedStorageProvider userFederatedStorage();
+
+
+    /**
      * Keycloak scripting support.
      */
     ScriptingProvider scripting();
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 af4d6d7..47692ac 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
@@ -18,13 +18,17 @@
 package org.keycloak.models;
 
 import org.keycloak.common.enums.SslRequired;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.provider.ProviderEvent;
-import org.keycloak.storage.StorageProviderModel;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.storage.UserStorageProviderModel;
 
 import java.security.Key;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -282,12 +286,23 @@ public interface RealmModel extends RoleContainerModel {
     public IdentityProviderMapperModel getIdentityProviderMapperByName(String brokerAlias, String name);
 
 
-    StorageProviderModel addStorageProvider(StorageProviderModel model);
-    void updateStorageProvider(StorageProviderModel provider);
-    void removeStorageProvider(StorageProviderModel provider);
-    void setStorageProviders(List<StorageProviderModel> providers);
-    List<StorageProviderModel> getStorageProviders();
-    StorageProviderModel getStorageProvider(String id);
+    ComponentModel addComponentModel(ComponentModel model);
+    void updateComponent(ComponentModel component);
+    void removeComponent(ComponentModel component);
+    void removeComponents(String parentId);
+    List<ComponentModel> getComponents(String parentId, String providerType);
+    List<ComponentModel> getComponents();
+    ComponentModel getComponent(String id);
+
+    default
+    List<UserStorageProviderModel> getUserStorageProviders() {
+        List<UserStorageProviderModel> list = new LinkedList<>();
+        for (ComponentModel component : getComponents(getId(), UserStorageProvider.class.getName())) {
+            list.add(new UserStorageProviderModel(component));
+        }
+        Collections.sort(list, UserStorageProviderModel.comparator);
+        return list;
+    }
 
     // Should return list sorted by UserFederationProviderModel.priority
     List<UserFederationProviderModel> getUserFederationProviders();
diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
index 19db2e1..c48ae4d 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -18,11 +18,11 @@
 package org.keycloak.models;
 
 import org.jboss.logging.Logger;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.policy.PasswordPolicyManagerProvider;
 import org.keycloak.policy.PolicyError;
 import org.keycloak.services.managers.UserManager;
-import org.keycloak.storage.StorageProviderModel;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -487,11 +487,6 @@ public class UserFederationManager implements UserProvider {
         session.userStorage().preRemove(protocolMapper);
     }
 
-    @Override
-    public void preRemove(RealmModel realm, StorageProviderModel link) {
-
-    }
-
     public void updateCredential(RealmModel realm, UserModel user, UserCredentialModel credential) {
         if (credential.getType().equals(UserCredentialModel.PASSWORD)) {
             if (realm.getPasswordPolicy() != null) {
@@ -605,6 +600,11 @@ public class UserFederationManager implements UserProvider {
     }
 
     @Override
+    public void preRemove(RealmModel realm, ComponentModel component) {
+
+    }
+
+    @Override
     public void close() {
     }
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/UserProvider.java b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
index 24b6106..d6ef2cd 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
@@ -17,8 +17,8 @@
 
 package org.keycloak.models;
 
+import org.keycloak.component.ComponentModel;
 import org.keycloak.provider.Provider;
-import org.keycloak.storage.StorageProviderModel;
 import org.keycloak.storage.user.UserCredentialValidatorProvider;
 import org.keycloak.storage.user.UserLookupProvider;
 import org.keycloak.storage.user.UserQueryProvider;
@@ -56,10 +56,20 @@ public interface UserProvider extends Provider,
     List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts);
     List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts);
 
+    /**
+     * only used for local storage
+     *
+     * @param realm
+     * @param id
+     * @param username
+     * @param addDefaultRoles
+     * @param addDefaultRequiredActions
+     * @return
+     */
+    UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles, boolean addDefaultRequiredActions);
     void preRemove(RealmModel realm);
 
     void preRemove(RealmModel realm, UserFederationProviderModel link);
-    void preRemove(RealmModel realm, StorageProviderModel link);
 
     void preRemove(RealmModel realm, RoleModel role);
     void preRemove(RealmModel realm, GroupModel group);
@@ -73,4 +83,6 @@ public interface UserProvider extends Provider,
 
 
     void close();
+
+    void preRemove(RealmModel realm, ComponentModel component);
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index 55a7c02..bc20d49 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -44,7 +44,6 @@ import org.keycloak.models.UserModel;
 import org.keycloak.representations.idm.CertificateRepresentation;
 import org.keycloak.common.util.CertificateUtils;
 import org.keycloak.common.util.PemUtils;
-import org.keycloak.storage.StorageProviderModel;
 
 import javax.crypto.spec.SecretKeySpec;
 import java.io.IOException;
@@ -282,7 +281,7 @@ public final class KeycloakModelUtils {
      */
     public static void runJobInTransaction(KeycloakSessionFactory factory, KeycloakSessionTask task) {
         KeycloakSession session = factory.create();
-        KeycloakTransaction tx = session.getTransaction();
+        KeycloakTransaction tx = session.getTransactionManager();
         try {
             tx.begin();
             task.run(session);
@@ -350,43 +349,6 @@ public final class KeycloakModelUtils {
      * @param federationProviders
      * @throws ModelDuplicateException if there is other provider with same displayName
      */
-    public static void ensureUniqueDisplayName(String displayName, StorageProviderModel myProvider, List<StorageProviderModel> federationProviders) throws ModelDuplicateException {
-        if (displayName != null) {
-
-            for (StorageProviderModel federationProvider : federationProviders) {
-                if (myProvider != null && (myProvider.equals(federationProvider) || (myProvider.getId() != null && myProvider.getId().equals(federationProvider.getId())))) {
-                    continue;
-                }
-
-                if (displayName.equals(federationProvider.getDisplayName())) {
-                    throw new ModelDuplicateException("There is already existing federation provider with display name: " + displayName);
-                }
-            }
-        }
-    }
-
-
-    public static StorageProviderModel findStorageProviderByDisplayName(String displayName, RealmModel realm) {
-        if (displayName == null) {
-            return null;
-        }
-
-        for (StorageProviderModel provider : realm.getStorageProviders()) {
-            if (displayName.equals(provider.getDisplayName())) {
-                return provider;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Ensure that displayName of myProvider (if not null) is unique and there is no other provider with same displayName in the list.
-     *
-     * @param displayName         to check for duplications
-     * @param myProvider          provider, which is excluded from the list (if present)
-     * @param federationProviders
-     * @throws ModelDuplicateException if there is other provider with same displayName
-     */
     public static void ensureUniqueDisplayName(String displayName, UserFederationProviderModel myProvider, List<UserFederationProviderModel> federationProviders) throws ModelDuplicateException {
         if (displayName != null) {
 
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 203736f..f0c75b7 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
@@ -17,6 +17,7 @@
 
 package org.keycloak.models.utils;
 
+import org.keycloak.component.ComponentModel;
 import org.keycloak.events.Event;
 import org.keycloak.events.admin.AdminEvent;
 import org.keycloak.events.admin.AuthDetails;
@@ -44,6 +45,7 @@ import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.idm.AdminEventRepresentation;
 import org.keycloak.representations.idm.AuthDetailsRepresentation;
 import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
@@ -51,6 +53,8 @@ import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
 import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ComponentRepresentation;
+import org.keycloak.representations.idm.ConfigPropertyRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.EventRepresentation;
 import org.keycloak.representations.idm.FederatedIdentityRepresentation;
@@ -565,7 +569,7 @@ public class ModelToRepresentation {
         return rep;
     }
 
-    public static UserFederationMapperRepresentation toRepresentation(RealmModel realm, UserFederationMapperModel model) {
+     public static UserFederationMapperRepresentation toRepresentation(RealmModel realm, UserFederationMapperModel model) {
         UserFederationMapperRepresentation rep = new UserFederationMapperRepresentation();
         rep.setId(model.getId());
         rep.setName(model.getName());
@@ -736,4 +740,27 @@ public class ModelToRepresentation {
         return rep;
     }
 
+    public static List<ConfigPropertyRepresentation> toRepresentation(List<ProviderConfigProperty> configProperties) {
+        List<ConfigPropertyRepresentation> propertiesRep = new LinkedList<>();
+        for (ProviderConfigProperty prop : configProperties) {
+            ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
+            propRep.setName(prop.getName());
+            propRep.setLabel(prop.getLabel());
+            propRep.setType(prop.getType());
+            propRep.setDefaultValue(prop.getDefaultValue());
+            propRep.setHelpText(prop.getHelpText());
+            propertiesRep.add(propRep);
+        }
+        return propertiesRep;
+    }
+
+    public static ComponentRepresentation toRepresentation(ComponentModel component) {
+        ComponentRepresentation rep = new ComponentRepresentation();
+        rep.setId(component.getId());
+        rep.setName(component.getName());
+        rep.setProviderId(component.getProviderId());
+        rep.setProviderType(component.getProviderType());
+        rep.setConfig(component.getConfig());
+        return rep;
+    }
 }
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 3efca79..a20adcf 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
@@ -20,7 +20,9 @@ package org.keycloak.models.utils;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.hash.Pbkdf2PasswordHashProvider;
+import org.keycloak.migration.migrators.MigrationUtils;
 import org.keycloak.models.ClientTemplateModel;
 import org.keycloak.models.Constants;
 import org.keycloak.common.util.Base64;
@@ -61,6 +63,7 @@ import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
 import org.keycloak.representations.idm.ClaimRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ComponentRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.FederatedIdentityRepresentation;
 import org.keycloak.representations.idm.GroupRepresentation;
@@ -204,6 +207,9 @@ public class RepresentationToModel {
         if (rep.getRequiredActions() != null) {
             for (RequiredActionProviderRepresentation action : rep.getRequiredActions()) {
                 RequiredActionProviderModel model = toModel(action);
+
+                MigrationUtils.updateOTPRequiredAction(model);
+
                 newRealm.addRequiredActionProvider(model);
             }
         } else {
@@ -1614,4 +1620,13 @@ public class RepresentationToModel {
     }
 
 
+    public static ComponentModel toModel(ComponentRepresentation rep) {
+        ComponentModel model = new ComponentModel();
+        model.setParentId(rep.getParentId());
+        model.setProviderType(rep.getProviderType());
+        model.setProviderId(rep.getProviderId());
+        model.setConfig(rep.getConfig());
+        model.setName(rep.getName());
+        return model;
+    }
 }
diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java
index 45c6168..6a4e7d2 100644
--- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java
+++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java
@@ -17,6 +17,7 @@
 package org.keycloak.storage.adapter;
 
 import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.GroupModel;
 import org.keycloak.models.KeycloakSession;
@@ -29,7 +30,6 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.DefaultRoles;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.storage.StorageId;
-import org.keycloak.storage.StorageProviderModel;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -58,9 +58,9 @@ public abstract class AbstractUserAdapter implements UserModel {
     }
     protected KeycloakSession session;
     protected RealmModel realm;
-    protected StorageProviderModel storageProviderModel;
+    protected ComponentModel storageProviderModel;
 
-    public AbstractUserAdapter(KeycloakSession session, RealmModel realm, StorageProviderModel storageProviderModel) {
+    public AbstractUserAdapter(KeycloakSession session, RealmModel realm, ComponentModel storageProviderModel) {
         this.session = session;
         this.realm = realm;
         this.storageProviderModel = storageProviderModel;
diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java
index b09ceae..bfe0b95 100644
--- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java
+++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java
@@ -16,6 +16,7 @@
  */
 package org.keycloak.storage.adapter;
 
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.GroupModel;
 import org.keycloak.models.KeycloakSession;
@@ -28,11 +29,9 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.DefaultRoles;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.storage.StorageId;
-import org.keycloak.storage.StorageProviderModel;
 import org.keycloak.storage.federated.UserFederatedStorageProvider;
 
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -60,9 +59,9 @@ public abstract class AbstractUserAdapterFederatedStorage implements UserModel {
 
     protected KeycloakSession session;
     protected RealmModel realm;
-    protected StorageProviderModel storageProviderModel;
+    protected ComponentModel storageProviderModel;
 
-    public AbstractUserAdapterFederatedStorage(KeycloakSession session, RealmModel realm, StorageProviderModel storageProviderModel) {
+    public AbstractUserAdapterFederatedStorage(KeycloakSession session, RealmModel realm, ComponentModel storageProviderModel) {
         this.session = session;
         this.realm = realm;
         this.storageProviderModel = storageProviderModel;
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
index 46ae9db..b604991 100755
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.storage.federated;
 
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.GroupModel;
 import org.keycloak.models.ProtocolMapperModel;
@@ -25,7 +26,6 @@ import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.provider.Provider;
-import org.keycloak.storage.StorageProviderModel;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -44,7 +44,7 @@ public interface UserFederatedStorageProvider extends Provider,
 
     void preRemove(RealmModel realm, UserFederationProviderModel link);
 
-    public void preRemove(RealmModel realm, GroupModel group);
+    void preRemove(RealmModel realm, GroupModel group);
 
     void preRemove(RealmModel realm, RoleModel role);
 
@@ -54,5 +54,5 @@ public interface UserFederatedStorageProvider extends Provider,
 
     void preRemove(RealmModel realm, UserModel user);
 
-    void preRemove(RealmModel realm, StorageProviderModel model);
+    void preRemove(RealmModel realm, ComponentModel model);
 }
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProviderSpi.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProviderSpi.java
index 5475422..21b1ad0 100755
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProviderSpi.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProviderSpi.java
@@ -20,8 +20,6 @@ package org.keycloak.storage.federated;
 import org.keycloak.provider.Provider;
 import org.keycloak.provider.ProviderFactory;
 import org.keycloak.provider.Spi;
-import org.keycloak.storage.StorageProvider;
-import org.keycloak.storage.StorageProviderFactory;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
diff --git a/server-spi/src/main/java/org/keycloak/storage/StorageId.java b/server-spi/src/main/java/org/keycloak/storage/StorageId.java
index 5620740..ab44e47 100644
--- a/server-spi/src/main/java/org/keycloak/storage/StorageId.java
+++ b/server-spi/src/main/java/org/keycloak/storage/StorageId.java
@@ -16,6 +16,7 @@
  */
 package org.keycloak.storage;
 
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.UserModel;
 
 import java.io.Serializable;
@@ -27,27 +28,44 @@ import java.io.Serializable;
 public class StorageId implements Serializable {
     private String id;
     private String providerId;
-    private String storageId;
+    private String externalId;
 
 
     public StorageId(String id) {
         this.id = id;
         if (!id.startsWith("f:")) {
-            storageId = id;
+            externalId = id;
             return;
         }
         int providerIndex = id.indexOf(':', 2);
         providerId = id.substring(2, providerIndex);
-        storageId = id.substring(providerIndex + 1);
+        externalId = id.substring(providerIndex + 1);
 
     }
 
-    public StorageId(String providerId, String storageId) {
-        this.id = "f:" + providerId + ":" + storageId;
+    public StorageId(String providerId, String externalId) {
+        this.id = "f:" + providerId + ":" + externalId;
         this.providerId = providerId;
-        this.storageId = storageId;
+        this.externalId = externalId;
     }
 
+    /**
+     * generate the id string that should be returned by UserModel.getId()
+     *
+     * @param model
+     * @param externalId id used to resolve user in external storage
+     * @return
+     */
+    public static String keycloakId(ComponentModel model, String externalId) {
+        return new StorageId(model.getId(), externalId).getId();
+    }
+
+    public static String externalId(String keycloakId) {
+        return new StorageId(keycloakId).getExternalId();
+    }
+
+
+
     public static String resolveProviderId(UserModel user) {
         return new StorageId(user.getId()).getProviderId();
     }
@@ -63,8 +81,8 @@ public class StorageId implements Serializable {
         return providerId;
     }
 
-    public String getStorageId() {
-        return storageId;
+    public String getExternalId() {
+        return externalId;
     }
 
 
diff --git a/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java b/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java
index 7b6922d..7697042 100644
--- a/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java
+++ b/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java
@@ -25,7 +25,6 @@ import org.keycloak.models.UserModel;
  * @version $Revision: 1 $
  */
 public interface UserRegistrationProvider {
-    UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles, boolean addDefaultRequiredActions);
 
     UserModel addUser(RealmModel realm, String username);
 
diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageManager.java b/server-spi/src/main/java/org/keycloak/storage/UserStorageManager.java
index b6e6028..72906b0 100755
--- a/server-spi/src/main/java/org/keycloak/storage/UserStorageManager.java
+++ b/server-spi/src/main/java/org/keycloak/storage/UserStorageManager.java
@@ -19,6 +19,7 @@ package org.keycloak.storage;
 
 import org.jboss.logging.Logger;
 import org.keycloak.common.util.reflections.Types;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.FederatedIdentityModel;
@@ -62,10 +63,6 @@ public class UserStorageManager implements UserProvider {
 
     protected KeycloakSession session;
 
-    // Set of already validated/proxied federation users during this session. Key is user ID
-    private Map<String, UserModel> managedUsers = new HashMap<>();
-    private UserProvider localStorage = null;
-
     public UserStorageManager(KeycloakSession session) {
         this.session = session;
     }
@@ -74,28 +71,37 @@ public class UserStorageManager implements UserProvider {
         return session.userLocalStorage();
     }
 
-    protected List<StorageProviderModel> getStorageProviders(RealmModel realm) {
-        return realm.getStorageProviders();
+    protected List<UserStorageProviderModel> getStorageProviders(RealmModel realm) {
+        return realm.getUserStorageProviders();
     }
 
     protected <T> T getFirstStorageProvider(RealmModel realm, Class<T> type) {
-        for (StorageProviderModel model : getStorageProviders(realm)) {
-            StorageProviderFactory factory = (StorageProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(StorageProvider.class, model.getProviderName());
+        for (UserStorageProviderModel model : getStorageProviders(realm)) {
+            UserStorageProviderFactory factory = (UserStorageProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, model.getProviderId());
 
-            if (Types.supports(type, factory, StorageProviderFactory.class)) {
-                return type.cast(factory.getInstance(session, model));
+            if (Types.supports(type, factory, UserStorageProviderFactory.class)) {
+                return type.cast(getStorageProviderInstance(model, factory));
             }
         }
         return null;
     }
 
+    private UserStorageProvider getStorageProviderInstance(UserStorageProviderModel model, UserStorageProviderFactory factory) {
+        UserStorageProvider instance = (UserStorageProvider)session.getAttribute(model.getId());
+        if (instance != null) return instance;
+        instance = factory.create(session, model);
+        session.enlistForClose(instance);
+        session.setAttribute(model.getId(), instance);
+        return instance;
+    }
+
 
     protected <T> List<T> getStorageProviders(RealmModel realm, Class<T> type) {
         List<T> list = new LinkedList<>();
-        for (StorageProviderModel model : getStorageProviders(realm)) {
-            StorageProviderFactory factory = (StorageProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(StorageProvider.class, model.getProviderName());
-            if (Types.supports(type, factory, StorageProviderFactory.class)) {
-                list.add(type.cast(factory.getInstance(session, model)));
+        for (UserStorageProviderModel model : getStorageProviders(realm)) {
+            UserStorageProviderFactory factory = (UserStorageProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, model.getProviderId());
+            if (Types.supports(type, factory, UserStorageProviderFactory.class)) {
+                list.add(type.cast(getStorageProviderInstance(model, factory)));
             }
 
 
@@ -106,10 +112,6 @@ public class UserStorageManager implements UserProvider {
 
     @Override
     public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles, boolean addDefaultRequiredActions) {
-        UserRegistrationProvider registry = getFirstStorageProvider(realm, UserRegistrationProvider.class);
-        if (registry != null) {
-            return registry.addUser(realm, id, username, addDefaultRoles, addDefaultRequiredActions);
-        }
         return localStorage().addUser(realm, id, username.toLowerCase(), addDefaultRoles, addDefaultRequiredActions);
     }
 
@@ -122,14 +124,14 @@ public class UserStorageManager implements UserProvider {
         return localStorage().addUser(realm, username.toLowerCase());
     }
 
-    public StorageProvider getStorageProvider(RealmModel realm, String providerId) {
-        StorageProviderModel model = realm.getStorageProvider(providerId);
+    public UserStorageProvider getStorageProvider(RealmModel realm, String componentId) {
+        ComponentModel model = realm.getComponent(componentId);
         if (model == null) return null;
-        StorageProviderFactory factory = (StorageProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(StorageProvider.class, model.getProviderName());
+        UserStorageProviderFactory factory = (UserStorageProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, model.getProviderId());
         if (factory == null) {
-            throw new ModelException("Could not find StorageProviderFactory for: " + model.getProviderName());
+            throw new ModelException("Could not find UserStorageProviderFactory for: " + model.getProviderId());
         }
-        return factory.getInstance(session, model);
+        return getStorageProviderInstance(new UserStorageProviderModel(model), factory);
     }
 
     @Override
@@ -481,7 +483,7 @@ public class UserStorageManager implements UserProvider {
     public void preRemove(RealmModel realm) {
         localStorage().preRemove(realm);
         getFederatedStorage().preRemove(realm);
-        for (StorageProvider provider : getStorageProviders(realm, StorageProvider.class)) {
+        for (UserStorageProvider provider : getStorageProviders(realm, UserStorageProvider.class)) {
             provider.preRemove(realm);
         }
     }
@@ -496,7 +498,7 @@ public class UserStorageManager implements UserProvider {
     public void preRemove(RealmModel realm, GroupModel group) {
         localStorage().preRemove(realm, group);
         getFederatedStorage().preRemove(realm, group);
-        for (StorageProvider provider : getStorageProviders(realm, StorageProvider.class)) {
+        for (UserStorageProvider provider : getStorageProviders(realm, UserStorageProvider.class)) {
             provider.preRemove(realm, group);
         }
     }
@@ -505,7 +507,7 @@ public class UserStorageManager implements UserProvider {
     public void preRemove(RealmModel realm, RoleModel role) {
         localStorage().preRemove(realm, role);
         getFederatedStorage().preRemove(realm, role);
-        for (StorageProvider provider : getStorageProviders(realm, StorageProvider.class)) {
+        for (UserStorageProvider provider : getStorageProviders(realm, UserStorageProvider.class)) {
             provider.preRemove(realm, role);
         }
     }
@@ -557,7 +559,7 @@ public class UserStorageManager implements UserProvider {
 
         if (toValidate.isEmpty()) return true;
 
-        StorageProvider provider = getStorageProvider(realm, StorageId.resolveProviderId(user));
+        UserStorageProvider provider = getStorageProvider(realm, StorageId.resolveProviderId(user));
         if (!(provider instanceof UserCredentialValidatorProvider)) {
             return false;
         }
@@ -601,7 +603,7 @@ public class UserStorageManager implements UserProvider {
     }
 
     @Override
-    public void preRemove(RealmModel realm, StorageProviderModel link) {
+    public void preRemove(RealmModel realm, ComponentModel component) {
 
     }
 
diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProvider.java b/server-spi/src/main/java/org/keycloak/storage/UserStorageProvider.java
index 58695c3..dbb4b3c 100644
--- a/server-spi/src/main/java/org/keycloak/storage/UserStorageProvider.java
+++ b/server-spi/src/main/java/org/keycloak/storage/UserStorageProvider.java
@@ -16,13 +16,18 @@
  */
 package org.keycloak.storage;
 
+import org.keycloak.models.GroupModel;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.provider.Provider;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public interface UserStorageProvider {
+public interface UserStorageProvider extends Provider {
     void preRemove(RealmModel realm);
-
+    void preRemove(RealmModel realm, GroupModel group);
+    void preRemove(RealmModel realm, RoleModel role);
 }
+
diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java
new file mode 100755
index 0000000..351107f
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java
@@ -0,0 +1,62 @@
+/*
+ * 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.storage;
+
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.RealmModel;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Stored configuration of a User Storage provider instance.
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+public class UserStorageProviderModel extends ComponentModel {
+
+    public static Comparator<UserStorageProviderModel> comparator = new Comparator<UserStorageProviderModel>() {
+        @Override
+        public int compare(UserStorageProviderModel o1, UserStorageProviderModel o2) {
+            return o1.getPriority() - o2.getPriority();
+        }
+    };
+
+    public UserStorageProviderModel() {
+        setProviderType(UserStorageProvider.class.getName());
+    }
+
+    public UserStorageProviderModel(ComponentModel copy) {
+        super(copy);
+    }
+
+    public int getPriority() {
+        String priority = getConfig().getFirst("priority");
+        if (priority == null) return 0;
+        return Integer.valueOf(priority);
+
+    }
+
+    public void setPriority(int priority) {
+        getConfig().putSingle("priority", Integer.toString(priority));
+    }
+}
diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 696bb4d..d2431d8 100755
--- a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -16,7 +16,7 @@
 #
 
 org.keycloak.models.UserFederationSpi
-org.keycloak.storage.StorageProviderSpi
+org.keycloak.storage.UserStorageProviderSpi
 org.keycloak.storage.federated.UserFederatedStorageProviderSpi
 org.keycloak.mappers.UserFederationMapperSpi
 org.keycloak.models.RealmSpi
diff --git a/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java b/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java
index 59d6217..705d230 100644
--- a/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java
+++ b/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java
@@ -62,7 +62,7 @@ public class PartialImportManager {
             try {
                 partialImport.prepare(rep, realm, session);
             } catch (ErrorResponseException error) {
-                if (session.getTransaction().isActive()) session.getTransaction().setRollbackOnly();
+                if (session.getTransactionManager().isActive()) session.getTransactionManager().setRollbackOnly();
                 return error.getResponse();
             }
         }
@@ -72,7 +72,7 @@ public class PartialImportManager {
                 partialImport.removeOverwrites(realm, session);
                 results.addAllResults(partialImport.doImport(rep, realm, session));
             } catch (ErrorResponseException error) {
-                if (session.getTransaction().isActive()) session.getTransaction().setRollbackOnly();
+                if (session.getTransactionManager().isActive()) session.getTransactionManager().setRollbackOnly();
                 return error.getResponse();
             }
         }
@@ -84,8 +84,8 @@ public class PartialImportManager {
             }
         }
 
-        if (session.getTransaction().isActive()) {
-            session.getTransaction().commit();
+        if (session.getTransactionManager().isActive()) {
+            session.getTransactionManager().commit();
         }
 
         return Response.ok(results).build();
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
index 8eb2a56..1fae787 100644
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
@@ -36,6 +36,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
     private final Map<Integer, Provider> providers = new HashMap<>();
     private final List<Provider> closable = new LinkedList<Provider>();
     private final DefaultKeycloakTransactionManager transactionManager;
+    private final Map<String, Object> attributes = new HashMap<>();
     private RealmProvider model;
     private UserProvider userModel;
     private UserStorageManager userStorageManager;
@@ -81,7 +82,22 @@ public class DefaultKeycloakSession implements KeycloakSession {
     }
 
     @Override
-    public KeycloakTransactionManager getTransaction() {
+    public Object getAttribute(String attribute) {
+        return attributes.get(attribute);
+    }
+
+    @Override
+    public Object removeAttribute(String attribute) {
+        return attributes.remove(attribute);
+    }
+
+    @Override
+    public void setAttribute(String name, Object value) {
+        attributes.put(name, value);
+    }
+
+    @Override
+    public KeycloakTransactionManager getTransactionManager() {
         return transactionManager;
     }
 
diff --git a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
index 40c1ac6..4187645 100755
--- a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
+++ b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
@@ -82,7 +82,7 @@ public class KeycloakSessionServletFilter implements Filter {
         session.getContext().setConnection(connection);
         ResteasyProviderFactory.pushContext(ClientConnection.class, connection);
 
-        KeycloakTransaction tx = session.getTransaction();
+        KeycloakTransaction tx = session.getTransactionManager();
         ResteasyProviderFactory.pushContext(KeycloakTransaction.class, tx);
         tx.begin();
 
@@ -123,8 +123,8 @@ public class KeycloakSessionServletFilter implements Filter {
     private void closeSession(KeycloakSession session) {
         // KeycloakTransactionCommitter is responsible for committing the transaction, but if an exception is thrown it's not invoked and transaction
         // should be rolled back
-        if (session.getTransaction() != null && session.getTransaction().isActive()) {
-            session.getTransaction().rollback();
+        if (session.getTransactionManager() != null && session.getTransactionManager().isActive()) {
+            session.getTransactionManager().rollback();
         }
 
         session.close();
diff --git a/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java b/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java
index fa3b1d5..28fc29d 100644
--- a/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java
+++ b/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java
@@ -177,7 +177,7 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
                         queue.drainTo(events, TRANSACTION_SIZE);
                         Collections.sort(events); // we sort to avoid deadlock due to ordered updates.  Maybe I'm overthinking this.
                         KeycloakSession session = factory.create();
-                        session.getTransaction().begin();
+                        session.getTransactionManager().begin();
                         try {
                             for (LoginEvent event : events) {
                                 if (event instanceof FailedLogin) {
@@ -186,9 +186,9 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector
                                     run = false;
                                 }
                             }
-                            session.getTransaction().commit();
+                            session.getTransactionManager().commit();
                         } catch (Exception e) {
-                            session.getTransaction().rollback();
+                            session.getTransactionManager().rollback();
                             throw e;
                         } finally {
                             for (LoginEvent event : events) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
index 94203d7..82e65fb 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
@@ -536,7 +536,7 @@ public class AuthenticationManagementResource {
 
         AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(rep.getId());
         if (model == null) {
-            session.getTransaction().setRollbackOnly();
+            session.getTransactionManager().setRollbackOnly();
             throw new NotFoundException("Illegal execution");
 
         }
@@ -596,7 +596,7 @@ public class AuthenticationManagementResource {
 
         AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
         if (model == null) {
-            session.getTransaction().setRollbackOnly();
+            session.getTransactionManager().setRollbackOnly();
             throw new NotFoundException("Illegal execution");
 
         }
@@ -642,7 +642,7 @@ public class AuthenticationManagementResource {
 
         AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
         if (model == null) {
-            session.getTransaction().setRollbackOnly();
+            session.getTransactionManager().setRollbackOnly();
             throw new NotFoundException("Illegal execution");
 
         }
@@ -682,7 +682,7 @@ public class AuthenticationManagementResource {
 
         AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
         if (model == null) {
-            session.getTransaction().setRollbackOnly();
+            session.getTransactionManager().setRollbackOnly();
             throw new NotFoundException("Illegal execution");
 
         }
@@ -718,7 +718,7 @@ public class AuthenticationManagementResource {
 
         AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
         if (model == null) {
-            session.getTransaction().setRollbackOnly();
+            session.getTransactionManager().setRollbackOnly();
             throw new NotFoundException("Illegal execution");
 
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
index a2c35d8..de4d49b 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
@@ -73,8 +73,8 @@ public class ClientInitialAccessResource {
 
         adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, clientInitialAccessModel.getId()).representation(config).success();
 
-        if (session.getTransaction().isActive()) {
-            session.getTransaction().commit();
+        if (session.getTransactionManager().isActive()) {
+            session.getTransactionManager().commit();
         }
 
         ClientInitialAccessPresentation rep = wrap(clientInitialAccessModel);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java
new file mode 100644
index 0000000..0097b19
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java
@@ -0,0 +1,152 @@
+/*
+ * 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.services.resources.admin;
+
+import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.common.ClientConnection;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.events.admin.OperationType;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.representations.idm.ComponentRepresentation;
+import org.keycloak.services.ServicesLogger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ComponentResource {
+    protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
+
+    protected RealmModel realm;
+
+    private RealmAuth auth;
+
+    private AdminEventBuilder adminEvent;
+
+    @Context
+    protected ClientConnection clientConnection;
+
+    @Context
+    protected UriInfo uriInfo;
+
+    @Context
+    protected KeycloakSession session;
+
+    @Context
+    protected HttpHeaders headers;
+
+    public ComponentResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+        this.auth = auth;
+        this.realm = realm;
+        this.adminEvent = adminEvent;
+
+        auth.init(RealmAuth.Resource.USER);
+    }
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<ComponentRepresentation> getComponents(@QueryParam("parent") String parent, @QueryParam("type") String type) {
+        auth.requireManage();
+        if (parent == null) parent = realm.getId();
+        List<ComponentModel> components = realm.getComponents(parent, type);
+        List<ComponentRepresentation> reps = new LinkedList<>();
+        for (ComponentModel component : components) {
+            ComponentRepresentation rep = ModelToRepresentation.toRepresentation(component);
+            reps.add(rep);
+        }
+        return reps;
+    }
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response create(ComponentRepresentation rep) {
+        auth.requireManage();
+        ComponentModel model = RepresentationToModel.toModel(rep);
+        if (model.getParentId() == null) model.setParentId(realm.getId());
+        adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, model.getId()).representation(rep).success();
+
+
+
+        model = realm.addComponentModel(model);
+        return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
+    }
+
+    @GET
+    @Path("{id}")
+    public ComponentRepresentation getComponent(@PathParam("id") String id) {
+        auth.requireManage();
+        ComponentModel model = realm.getComponent(id);
+        if (model == null) {
+            throw new NotFoundException("Could not find component");
+        }
+        return ModelToRepresentation.toRepresentation(model);
+
+
+    }
+
+    @PUT
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void updateComponent(@PathParam("id") String id, ComponentRepresentation rep) {
+        auth.requireManage();
+        ComponentModel model = realm.getComponent(id);
+        if (model == null) {
+            throw new NotFoundException("Could not find component");
+        }
+        model = RepresentationToModel.toModel(rep);
+        model.setId(id);
+        adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo, model.getId()).representation(rep).success();
+        realm.updateComponent(model);
+
+    }
+    @DELETE
+    @Path("{id}")
+    public void removeComponent(@PathParam("id") String id) {
+        auth.requireManage();
+        ComponentModel model = realm.getComponent(id);
+        if (model == null) {
+            throw new NotFoundException("Could not find component");
+        }
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo, model.getId()).success();
+        realm.removeComponent(model);
+
+    }
+
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
index 1a67fb0..1a1d2bf 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
@@ -25,7 +25,6 @@ import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.ServiceLoader;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.WebApplicationException;
@@ -33,12 +32,13 @@ import javax.ws.rs.core.Context;
 
 import org.keycloak.broker.provider.IdentityProvider;
 import org.keycloak.broker.provider.IdentityProviderFactory;
+import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.events.EventType;
 import org.keycloak.events.admin.OperationType;
-import org.keycloak.models.PasswordPolicy;
 import org.keycloak.policy.PasswordPolicyProvider;
 import org.keycloak.policy.PasswordPolicyProviderFactory;
 import org.keycloak.provider.*;
+import org.keycloak.representations.idm.ComponentTypeRepresentation;
 import org.keycloak.representations.idm.PasswordPolicyTypeRepresentation;
 import org.keycloak.theme.Theme;
 import org.keycloak.theme.ThemeProvider;
@@ -115,12 +115,28 @@ public class ServerInfoAdminResource {
             Map<String, ProviderRepresentation> providers = new HashMap<>();
 
             if (providerIds != null) {
+                info.setComponentTypes(new HashMap<>());
                 for (String name : providerIds) {
                     ProviderRepresentation provider = new ProviderRepresentation();
                     ProviderFactory<?> pi = session.getKeycloakSessionFactory().getProviderFactory(spi.getProviderClass(), name);
                     if (ServerInfoAwareProviderFactory.class.isAssignableFrom(pi.getClass())) {
                         provider.setOperationalInfo(((ServerInfoAwareProviderFactory) pi).getOperationalInfo());
                     }
+                    if (pi instanceof ConfiguredProvider) {
+                        ComponentTypeRepresentation rep = new ComponentTypeRepresentation();
+                        rep.setId(pi.getId());
+                        ConfiguredProvider configured = (ConfiguredProvider)pi;
+                        rep.setHelpText(configured.getHelpText());
+                        List<ProviderConfigProperty> configProperties = configured.getConfigProperties();
+                        if (configProperties == null) configProperties = Collections.EMPTY_LIST;
+                        rep.setProperties(ModelToRepresentation.toRepresentation(configProperties));
+                        List<ComponentTypeRepresentation> reps = info.getComponentTypes().get(spi.getProviderClass().getName());
+                        if (reps == null) {
+                            reps = new LinkedList<>();
+                            info.getComponentTypes().put(spi.getProviderClass().getName(), reps);
+                        }
+                        reps.add(rep);
+                    }
                     providers.put(name, provider);
                 }
             }
@@ -225,15 +241,7 @@ public class ServerInfoAdminResource {
             rep.setCategory(mapper.getDisplayCategory());
             rep.setProperties(new LinkedList<ConfigPropertyRepresentation>());
             List<ProviderConfigProperty> configProperties = mapper.getConfigProperties();
-            for (ProviderConfigProperty prop : configProperties) {
-                ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
-                propRep.setName(prop.getName());
-                propRep.setLabel(prop.getLabel());
-                propRep.setType(prop.getType());
-                propRep.setDefaultValue(prop.getDefaultValue());
-                propRep.setHelpText(prop.getHelpText());
-                rep.getProperties().add(propRep);
-            }
+            rep.setProperties(ModelToRepresentation.toRepresentation(configProperties));
             types.add(rep);
         }
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 4594bdd..8270ed4 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -321,7 +321,7 @@ public class RealmAdminResource {
      */
     @Path("users")
     public UsersResource users() {
-        UsersResource users = new UsersResource(realm, auth, tokenManager, adminEvent);
+        UsersResource users = new UsersResource(realm, auth, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(users);
         //resourceContext.initResource(users);
         return users;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 5788b42..aa6aa68 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -127,7 +127,7 @@ public class UsersResource {
     @Context
     protected HttpHeaders headers;
 
-    public UsersResource(RealmModel realm, RealmAuth auth, TokenManager tokenManager, AdminEventBuilder adminEvent) {
+    public UsersResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
         this.auth = auth;
         this.realm = realm;
         this.adminEvent = adminEvent;
@@ -172,8 +172,8 @@ public class UsersResource {
             updateUserFromRep(user, rep, attrsToRemove, realm, session, true);
             adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
 
-            if (session.getTransaction().isActive()) {
-                session.getTransaction().commit();
+            if (session.getTransactionManager().isActive()) {
+                session.getTransactionManager().commit();
             }
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
@@ -214,19 +214,19 @@ public class UsersResource {
 
             adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, user.getId()).representation(rep).success();
 
-            if (session.getTransaction().isActive()) {
-                session.getTransaction().commit();
+            if (session.getTransactionManager().isActive()) {
+                session.getTransactionManager().commit();
             }
 
             return Response.created(uriInfo.getAbsolutePathBuilder().path(user.getId()).build()).build();
         } catch (ModelDuplicateException e) {
-            if (session.getTransaction().isActive()) {
-                session.getTransaction().setRollbackOnly();
+            if (session.getTransactionManager().isActive()) {
+                session.getTransactionManager().setRollbackOnly();
             }
             return ErrorResponse.exists("User exists with same username or email");
         } catch (ModelException me){
-            if (session.getTransaction().isActive()) {
-                session.getTransaction().setRollbackOnly();
+            if (session.getTransactionManager().isActive()) {
+                session.getTransactionManager().setRollbackOnly();
             }
             return ErrorResponse.exists("Could not create user");
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index c7cbc62..9181817 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -835,17 +835,17 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
 
     private void fireErrorEvent(String message, Throwable throwable) {
         if (!this.event.getEvent().getType().toString().endsWith("_ERROR")) {
-            boolean newTransaction = !this.session.getTransaction().isActive();
+            boolean newTransaction = !this.session.getTransactionManager().isActive();
 
             try {
                 if (newTransaction) {
-                    this.session.getTransaction().begin();
+                    this.session.getTransactionManager().begin();
                 }
 
                 this.event.error(message);
 
                 if (newTransaction) {
-                    this.session.getTransaction().commit();
+                    this.session.getTransactionManager().commit();
                 }
             } catch (Exception e) {
                 logger.couldNotFireEvent(e);
@@ -869,8 +869,8 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
     }
 
     private void rollback() {
-        if (this.session.getTransaction().isActive()) {
-            this.session.getTransaction().rollback();
+        if (this.session.getTransactionManager().isActive()) {
+            this.session.getTransactionManager().rollback();
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 0385281..5e1cd9f 100644
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -119,10 +119,10 @@ public class KeycloakApplication extends Application {
         boolean bootstrapAdminUser = false;
         KeycloakSession session = sessionFactory.create();
         try {
-            session.getTransaction().begin();
+            session.getTransactionManager().begin();
             bootstrapAdminUser = new ApplianceBootstrap(session).isNoMasterUser();
 
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
         } finally {
             session.close();
         }
@@ -142,7 +142,7 @@ public class KeycloakApplication extends Application {
 
         KeycloakSession session = sessionFactory.create();
         try {
-            session.getTransaction().begin();
+            session.getTransactionManager().begin();
 
             ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session);
             exportImportManager = new ExportImportManager(session);
@@ -155,10 +155,10 @@ public class KeycloakApplication extends Application {
             if (createMasterRealm) {
                 applianceBootstrap.createMasterRealm(contextPath);
             }
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
         } catch (RuntimeException re) {
-            if (session.getTransaction().isActive()) {
-                session.getTransaction().rollback();
+            if (session.getTransactionManager().isActive()) {
+                session.getTransactionManager().rollback();
             }
             throw re;
         } finally {
@@ -180,11 +180,11 @@ public class KeycloakApplication extends Application {
     protected void migrateModel() {
         KeycloakSession session = sessionFactory.create();
         try {
-            session.getTransaction().begin();
+            session.getTransactionManager().begin();
             MigrationModelManager.migrate(session);
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
         } catch (Exception e) {
-            session.getTransaction().rollback();
+            session.getTransactionManager().rollback();
             logger.migrationFailure(e);
             throw e;
         } finally {
@@ -294,7 +294,7 @@ public class KeycloakApplication extends Application {
         KeycloakSession session = sessionFactory.create();
         boolean exists = false;
         try {
-            session.getTransaction().begin();
+            session.getTransactionManager().begin();
 
             try {
                 RealmManager manager = new RealmManager(session);
@@ -313,9 +313,9 @@ public class KeycloakApplication extends Application {
                     RealmModel realm = manager.importRealm(rep);
                     logger.importedRealm(realm.getName(), from);
                 }
-                session.getTransaction().commit();
+                session.getTransactionManager().commit();
             } catch (Throwable t) {
-                session.getTransaction().rollback();
+                session.getTransactionManager().rollback();
                 if (!exists) {
                     logger.unableToImportRealm(t, rep.getRealm(), from);
                 }
@@ -345,7 +345,7 @@ public class KeycloakApplication extends Application {
                     for (UserRepresentation userRep : realmRep.getUsers()) {
                         KeycloakSession session = sessionFactory.create();
                         try {
-                            session.getTransaction().begin();
+                            session.getTransactionManager().begin();
 
                             RealmModel realm = session.realms().getRealmByName(realmRep.getRealm());
                             if (realm == null) {
@@ -357,13 +357,13 @@ public class KeycloakApplication extends Application {
                                 RepresentationToModel.createRoleMappings(userRep, user, realm);
                             }
 
-                            session.getTransaction().commit();
+                            session.getTransactionManager().commit();
                             logger.addUserSuccess(userRep.getUsername(), realmRep.getRealm());
                         } catch (ModelDuplicateException e) {
-                            session.getTransaction().rollback();
+                            session.getTransactionManager().rollback();
                             logger.addUserFailedUserExists(userRep.getUsername(), realmRep.getRealm());
                         } catch (Throwable t) {
-                            session.getTransaction().rollback();
+                            session.getTransactionManager().rollback();
                             logger.addUserFailed(t, userRep.getUsername(), realmRep.getRealm());
                         } finally {
                             session.close();
diff --git a/services/src/main/java/org/keycloak/services/scheduled/ClusterAwareScheduledTaskRunner.java b/services/src/main/java/org/keycloak/services/scheduled/ClusterAwareScheduledTaskRunner.java
index 94db9c9..b2a8bf7 100644
--- a/services/src/main/java/org/keycloak/services/scheduled/ClusterAwareScheduledTaskRunner.java
+++ b/services/src/main/java/org/keycloak/services/scheduled/ClusterAwareScheduledTaskRunner.java
@@ -41,7 +41,7 @@ public class ClusterAwareScheduledTaskRunner extends ScheduledTaskRunner {
 
     @Override
     protected void runTask(final KeycloakSession session) {
-        session.getTransaction().begin();
+        session.getTransactionManager().begin();
 
         ClusterProvider clusterProvider = session.getProvider(ClusterProvider.class);
         String taskKey = task.getClass().getSimpleName();
@@ -56,7 +56,7 @@ public class ClusterAwareScheduledTaskRunner extends ScheduledTaskRunner {
 
         });
 
-        session.getTransaction().commit();
+        session.getTransactionManager().commit();
 
         if (result.isExecuted()) {
             logger.debugf("Executed scheduled task %s", taskKey);
diff --git a/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java b/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java
index b49300f..81ff5f6 100644
--- a/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java
+++ b/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java
@@ -45,7 +45,7 @@ public class ScheduledTaskRunner implements Runnable {
         } catch (Throwable t) {
             logger.failedToRunScheduledTask(t, task.getClass().getSimpleName());
 
-            session.getTransaction().rollback();
+            session.getTransactionManager().rollback();
         } finally {
             try {
                 session.close();
@@ -56,9 +56,9 @@ public class ScheduledTaskRunner implements Runnable {
     }
 
     protected void runTask(KeycloakSession session) {
-        session.getTransaction().begin();
+        session.getTransactionManager().begin();
         task.run(session);
-        session.getTransaction().commit();
+        session.getTransactionManager().commit();
 
         logger.debug("Executed scheduled task " + task.getClass().getSimpleName());
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
index 507c0fe..adea44e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
@@ -265,7 +265,7 @@ public class AdapterTestStrategy extends ExternalResource {
         RealmModel realm = session.realms().getRealmByName("demo");
         int originalIdle = realm.getSsoSessionIdleTimeout();
         realm.setSsoSessionIdleTimeout(1);
-        session.getTransaction().commit();
+        session.getTransactionManager().commit();
         session.close();
 
         Time.setOffset(2);
@@ -278,7 +278,7 @@ public class AdapterTestStrategy extends ExternalResource {
         session = keycloakRule.startSession();
         realm = session.realms().getRealmByName("demo");
         realm.setSsoSessionIdleTimeout(originalIdle);
-        session.getTransaction().commit();
+        session.getTransactionManager().commit();
         session.close();
 
         Time.setOffset(0);
@@ -300,7 +300,7 @@ public class AdapterTestStrategy extends ExternalResource {
         RealmModel realm = session.realms().getRealmByName("demo");
         int originalIdle = realm.getSsoSessionIdleTimeout();
         realm.setSsoSessionIdleTimeout(1);
-        session.getTransaction().commit();
+        session.getTransactionManager().commit();
         session.close();
 
         Time.setOffset(2);
@@ -308,7 +308,7 @@ public class AdapterTestStrategy extends ExternalResource {
         session = keycloakRule.startSession();
         realm = session.realms().getRealmByName("demo");
         session.sessions().removeExpired(realm);
-        session.getTransaction().commit();
+        session.getTransactionManager().commit();
         session.close();
 
         // test SSO
@@ -321,7 +321,7 @@ public class AdapterTestStrategy extends ExternalResource {
         UserModel user = session.users().getUserByUsername("bburke@redhat.com", realm);
         new ResourceAdminManager(session).logoutUser(null, realm, user, session);
         realm.setSsoSessionIdleTimeout(originalIdle);
-        session.getTransaction().commit();
+        session.getTransactionManager().commit();
         session.close();
 
         Time.setOffset(0);
@@ -343,7 +343,7 @@ public class AdapterTestStrategy extends ExternalResource {
         RealmModel realm = session.realms().getRealmByName("demo");
         int original = realm.getSsoSessionMaxLifespan();
         realm.setSsoSessionMaxLifespan(1);
-        session.getTransaction().commit();
+        session.getTransactionManager().commit();
         session.close();
 
         Time.setOffset(2);
@@ -356,7 +356,7 @@ public class AdapterTestStrategy extends ExternalResource {
         session = keycloakRule.startSession();
         realm = session.realms().getRealmByName("demo");
         realm.setSsoSessionMaxLifespan(original);
-        session.getTransaction().commit();
+        session.getTransactionManager().commit();
         session.close();
 
         Time.setOffset(0);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/CookieTokenStoreAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/CookieTokenStoreAdapterTest.java
index 936dfe9..02e34a7 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/CookieTokenStoreAdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/CookieTokenStoreAdapterTest.java
@@ -125,7 +125,7 @@ public class CookieTokenStoreAdapterTest {
             RealmModel realm = session.realms().getRealmByName("demo");
             int originalTokenTimeout = realm.getAccessTokenLifespan();
             realm.setAccessTokenLifespan(3);
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
             session.close();
 
             // login to customer-cookie-portal
@@ -164,7 +164,7 @@ public class CookieTokenStoreAdapterTest {
             session = keycloakRule.startSession();
             realm = session.realms().getRealmByName("demo");
             realm.setAccessTokenLifespan(originalTokenTimeout);
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
             session.close();
         } finally {
             Time.setOffset(0);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractAuthorizationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractAuthorizationTest.java
index 301adca..fbf602e 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractAuthorizationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractAuthorizationTest.java
@@ -58,7 +58,7 @@ public abstract class AbstractAuthorizationTest {
 
     protected <R> R onAuthorizationSession(Function<AuthorizationProvider, R> function) {
         KeycloakSession keycloakSession = startKeycloakSession();
-        KeycloakTransactionManager transaction = keycloakSession.getTransaction();
+        KeycloakTransactionManager transaction = keycloakSession.getTransactionManager();
 
         try {
             AuthorizationProvider authorizationProvider = keycloakSession.getProvider(AuthorizationProvider.class);
@@ -80,7 +80,7 @@ public abstract class AbstractAuthorizationTest {
 
     protected void onAuthorizationSession(Consumer<AuthorizationProvider> consumer) {
         KeycloakSession keycloakSession = startKeycloakSession();
-        KeycloakTransactionManager transaction = keycloakSession.getTransaction();
+        KeycloakTransactionManager transaction = keycloakSession.getTransactionManager();
 
         try {
             AuthorizationProvider authorizationProvider = keycloakSession.getProvider(AuthorizationProvider.class);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorage.java
index daed4ad..a53479c 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorage.java
@@ -16,6 +16,7 @@
  */
 package org.keycloak.testsuite.federation.storage;
 
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.GroupModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
@@ -23,8 +24,7 @@ import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.storage.StorageId;
-import org.keycloak.storage.StorageProvider;
-import org.keycloak.storage.StorageProviderModel;
+import org.keycloak.storage.UserStorageProvider;
 import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
 import org.keycloak.storage.user.UserCredentialValidatorProvider;
 import org.keycloak.storage.user.UserLookupProvider;
@@ -32,28 +32,33 @@ import org.keycloak.storage.user.UserRegistrationProvider;
 
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class UserMapStorage implements UserLookupProvider, StorageProvider, UserCredentialValidatorProvider, UserRegistrationProvider {
+public class UserMapStorage implements UserLookupProvider, UserStorageProvider, UserCredentialValidatorProvider, UserRegistrationProvider {
 
     protected Map<String, String> userPasswords;
-    protected StorageProviderModel model;
+    protected ComponentModel model;
     protected KeycloakSession session;
 
-    public UserMapStorage(KeycloakSession session, StorageProviderModel model, Map<String, String> userPasswords) {
+    public static final AtomicInteger allocations = new AtomicInteger(0);
+    public static final AtomicInteger closings = new AtomicInteger(0);
+
+    public UserMapStorage(KeycloakSession session, ComponentModel model, Map<String, String> userPasswords) {
         this.session = session;
         this.model = model;
         this.userPasswords = userPasswords;
+        allocations.incrementAndGet();
     }
 
 
     @Override
     public UserModel getUserById(String id, RealmModel realm) {
         StorageId storageId = new StorageId(id);
-        final String username = storageId.getStorageId();
+        final String username = storageId.getExternalId();
         if (!userPasswords.containsKey(username)) return null;
 
         return createUser(realm, username);
@@ -95,12 +100,6 @@ public class UserMapStorage implements UserLookupProvider, StorageProvider, User
     }
 
     @Override
-    public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles, boolean addDefaultRequiredActions) {
-        userPasswords.put(username, "");
-        return createUser(realm, username);
-    }
-
-    @Override
     public UserModel addUser(RealmModel realm, String username) {
         userPasswords.put(username, "");
         return createUser(realm, username);
@@ -132,11 +131,6 @@ public class UserMapStorage implements UserLookupProvider, StorageProvider, User
     }
 
     @Override
-    public void preRemove(RealmModel realm, StorageProviderModel model) {
-
-    }
-
-    @Override
     public boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, List<UserCredentialModel> input) {
         for (UserCredentialModel cred : input) {
             if (!cred.getType().equals(UserCredentialModel.PASSWORD)) return false;
@@ -151,6 +145,7 @@ public class UserMapStorage implements UserLookupProvider, StorageProvider, User
 
     @Override
     public void close() {
+        closings.incrementAndGet();
 
     }
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorageFactory.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorageFactory.java
index f80a11d..79e9261 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorageFactory.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorageFactory.java
@@ -17,22 +17,19 @@
 package org.keycloak.testsuite.federation.storage;
 
 import org.keycloak.Config;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
-import org.keycloak.storage.StorageProvider;
-import org.keycloak.storage.StorageProviderFactory;
-import org.keycloak.storage.StorageProviderModel;
+import org.keycloak.storage.UserStorageProviderFactory;
 
-import java.io.IOException;
 import java.util.Hashtable;
 import java.util.Map;
-import java.util.Properties;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class UserMapStorageFactory implements StorageProviderFactory<UserMapStorage> {
+public class UserMapStorageFactory implements UserStorageProviderFactory<UserMapStorage> {
 
 
     public static final String PROVIDER_ID = "user-password-map";
@@ -40,7 +37,7 @@ public class UserMapStorageFactory implements StorageProviderFactory<UserMapStor
     protected Map<String, String> userPasswords = new Hashtable<>();
 
     @Override
-    public UserMapStorage getInstance(KeycloakSession session, StorageProviderModel model) {
+    public UserMapStorage create(KeycloakSession session, ComponentModel model) {
         return new UserMapStorage(session, model, userPasswords);
     }
 
@@ -50,11 +47,6 @@ public class UserMapStorageFactory implements StorageProviderFactory<UserMapStor
     }
 
     @Override
-    public StorageProvider create(KeycloakSession session) {
-        return null;
-    }
-
-    @Override
     public void init(Config.Scope config) {
 
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorage.java
index cf237e4..644dcb8 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorage.java
@@ -16,6 +16,7 @@
  */
 package org.keycloak.testsuite.federation.storage;
 
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.GroupModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
@@ -23,11 +24,9 @@ import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.storage.StorageId;
-import org.keycloak.storage.StorageProvider;
-import org.keycloak.storage.StorageProviderModel;
+import org.keycloak.storage.UserStorageProvider;
 import org.keycloak.storage.adapter.AbstractUserAdapter;
 import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
-import org.keycloak.storage.federated.UserFederatedStorageProvider;
 import org.keycloak.storage.user.UserCredentialValidatorProvider;
 import org.keycloak.storage.user.UserLookupProvider;
 import org.keycloak.storage.user.UserQueryProvider;
@@ -42,14 +41,14 @@ import java.util.Properties;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class UserPropertyFileStorage implements UserLookupProvider, StorageProvider, UserCredentialValidatorProvider, UserQueryProvider {
+public class UserPropertyFileStorage implements UserLookupProvider, UserStorageProvider, UserCredentialValidatorProvider, UserQueryProvider {
 
     protected Properties userPasswords;
-    protected StorageProviderModel model;
+    protected ComponentModel model;
     protected KeycloakSession session;
     protected boolean federatedStorageEnabled;
 
-    public UserPropertyFileStorage(KeycloakSession session, StorageProviderModel model, Properties userPasswords) {
+    public UserPropertyFileStorage(KeycloakSession session, ComponentModel model, Properties userPasswords) {
         this.session = session;
         this.model = model;
         this.userPasswords = userPasswords;
@@ -60,7 +59,7 @@ public class UserPropertyFileStorage implements UserLookupProvider, StorageProvi
     @Override
     public UserModel getUserById(String id, RealmModel realm) {
         StorageId storageId = new StorageId(id);
-        final String username = storageId.getStorageId();
+        final String username = storageId.getExternalId();
         if (!userPasswords.containsKey(username)) return null;
 
         return createUser(realm, username);
@@ -117,11 +116,6 @@ public class UserPropertyFileStorage implements UserLookupProvider, StorageProvi
     }
 
     @Override
-    public void preRemove(RealmModel realm, StorageProviderModel model) {
-
-    }
-
-    @Override
     public boolean validCredentials(KeycloakSession session, RealmModel realm, UserModel user, List<UserCredentialModel> input) {
         for (UserCredentialModel cred : input) {
             if (!cred.getType().equals(UserCredentialModel.PASSWORD)) return false;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorageFactory.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorageFactory.java
index 82d91cf..0dec2bb 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorageFactory.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserPropertyFileStorageFactory.java
@@ -17,33 +17,28 @@
 package org.keycloak.testsuite.federation.storage;
 
 import org.keycloak.Config;
+import org.keycloak.component.ComponentModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
-import org.keycloak.provider.ProviderFactory;
-import org.keycloak.storage.StorageProvider;
-import org.keycloak.storage.StorageProviderFactory;
-import org.keycloak.storage.StorageProviderModel;
+import org.keycloak.storage.UserStorageProviderFactory;
 
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Properties;
-import java.util.Set;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class UserPropertyFileStorageFactory implements StorageProviderFactory<UserPropertyFileStorage> {
+public class UserPropertyFileStorageFactory implements UserStorageProviderFactory<UserPropertyFileStorage> {
 
 
     public static final String PROVIDER_ID = "user-password-props";
 
     @Override
-    public UserPropertyFileStorage getInstance(KeycloakSession session, StorageProviderModel model) {
+    public UserPropertyFileStorage create(KeycloakSession session, ComponentModel model) {
         Properties props = new Properties();
         try {
-            props.load(getClass().getResourceAsStream(model.getConfig().get("property.file")));
+            props.load(getClass().getResourceAsStream(model.getConfig().getFirst("property.file")));
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
@@ -56,11 +51,6 @@ public class UserPropertyFileStorageFactory implements StorageProviderFactory<Us
     }
 
     @Override
-    public StorageProvider create(KeycloakSession session) {
-        return null;
-    }
-
-    @Override
     public void init(Config.Scope config) {
 
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
index 7791c91..3b8d68b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
@@ -39,7 +39,6 @@ import org.keycloak.testsuite.util.cli.TestsuiteCLI;
 import org.keycloak.util.JsonSerialization;
 
 import javax.servlet.DispatcherType;
-import javax.ws.rs.core.Application;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -249,7 +248,7 @@ public class KeycloakServer {
 
     public void importRealm(RealmRepresentation rep) {
         KeycloakSession session = sessionFactory.create();;
-        session.getTransaction().begin();
+        session.getTransactionManager().begin();
 
         try {
             RealmManager manager = new RealmManager(session);
@@ -268,7 +267,7 @@ public class KeycloakServer {
 
             info("Imported realm " + realm.getName());
 
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
         } finally {
             session.close();
         }
@@ -278,11 +277,11 @@ public class KeycloakServer {
         if (System.getProperty("keycloak.createAdminUser", "true").equals("true")) {
             KeycloakSession session = sessionFactory.create();
             try {
-                session.getTransaction().begin();
+                session.getTransactionManager().begin();
                 if (new ApplianceBootstrap(session).isNoMasterUser()) {
                     new ApplianceBootstrap(session).createMasterRealmUser("admin", "admin");
                 }
-                session.getTransaction().commit();
+                session.getTransactionManager().commit();
             } finally {
                 session.close();
             }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java
index 0befa7c..f1409f4 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java
@@ -81,16 +81,16 @@ public class AbstractModelTest {
 
     protected void commit(boolean rollback) {
         if (rollback) {
-            session.getTransaction().rollback();
+            session.getTransactionManager().rollback();
         } else {
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
         }
         resetSession();
     }
 
     protected void resetSession() {
-        if (session.getTransaction().isActive()) {
-            session.getTransaction().rollback();
+        if (session.getTransactionManager().isActive()) {
+            session.getTransactionManager().rollback();
         }
         kc.stopSession(session, false);
         session = kc.startSession();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
index a28af56..eecf8c9 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
@@ -74,7 +74,7 @@ public class AdapterTest extends AbstractModelTest {
         realmModel.setAccessTokenLifespan(1000);
         realmModel.addDefaultRole("foo");
 
-        session.getTransaction().commit();
+        session.getTransactionManager().commit();
         resetSession();
 
         realmModel = realmManager.getRealm(realmModel.getId());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/CacheTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/CacheTest.java
index 0d7c9b0..c478375 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/CacheTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/CacheTest.java
@@ -105,7 +105,7 @@ public class CacheTest {
 
             assertNotNull(user2.getLastName());
         } finally {
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
             session.close();
         }
     }
@@ -121,7 +121,7 @@ public class CacheTest {
             RoleModel fooRole = client.addRole("foo-role");
             user.grantRole(fooRole);
         } finally {
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
             session.close();
         }
 
@@ -136,7 +136,7 @@ public class CacheTest {
             ClientModel client = realm.getClientByClientId("foo");
             realm.removeClient(client.getId());
         } finally {
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
             session.close();
         }
 
@@ -152,7 +152,7 @@ public class CacheTest {
 
             Assert.assertEquals(roles.size(), grantedRolesCount - 1);
         } finally {
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
             session.close();
         }
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/TransactionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/TransactionsTest.java
index 0d7e9e2..2edcfaa 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/TransactionsTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/TransactionsTest.java
@@ -35,14 +35,14 @@ public class TransactionsTest {
     public void testTransactionActive() {
         KeycloakSession session = kc.startSession();
 
-        Assert.assertTrue(session.getTransaction().isActive());
-        session.getTransaction().commit();
-        Assert.assertFalse(session.getTransaction().isActive());
-
-        session.getTransaction().begin();
-        Assert.assertTrue(session.getTransaction().isActive());
-        session.getTransaction().rollback();
-        Assert.assertFalse(session.getTransaction().isActive());
+        Assert.assertTrue(session.getTransactionManager().isActive());
+        session.getTransactionManager().commit();
+        Assert.assertFalse(session.getTransactionManager().isActive());
+
+        session.getTransactionManager().begin();
+        Assert.assertTrue(session.getTransactionManager().isActive());
+        session.getTransactionManager().rollback();
+        Assert.assertFalse(session.getTransactionManager().isActive());
 
         session.close();
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
index 83ea5ad..01f99fb 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
@@ -217,7 +217,7 @@ public class UserModelTest extends AbstractModelTest {
         UserModel user1 = session.users().addUser(realm, "user1");
 
         commit();
-
+        realm = session.realms().getRealmByName("original");
         List<UserModel> users = session.users().searchForUser("user", realm, 0, 7);
         Assert.assertTrue(users.contains(user1));
     }
@@ -238,6 +238,7 @@ public class UserModelTest extends AbstractModelTest {
         user3.setSingleAttribute("key2", "value21");
 
         commit();
+        realm = session.realms().getRealmByName("original");
 
         List<UserModel> users = session.users().searchForUserByUserAttribute("key1", "value1", realm);
         Assert.assertEquals(2, users.size());
@@ -284,6 +285,7 @@ public class UserModelTest extends AbstractModelTest {
 
         // Search and assert service account user not found
         realm = realmManager.getRealmByName("original");
+        client = realm.getClientByClientId("foo");
         UserModel searched = session.users().getServiceAccount(client);
         Assert.assertEquals(searched, user1);
         users = session.users().searchForUser("John Doe", realm);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
index 172c517..8315331 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
@@ -85,12 +85,12 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
 
     public UserRepresentation getUser(String realm, String name) {
         KeycloakSession session = server.getSessionFactory().create();
-        session.getTransaction().begin();
+        session.getTransactionManager().begin();
         try {
             RealmModel realmByName = session.realms().getRealmByName(realm);
             UserModel user = session.users().getUserByUsername(name, realmByName);
             UserRepresentation userRep = user != null ? ModelToRepresentation.toRepresentation(user) : null;
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
             return userRep;
         } finally {
             session.close();
@@ -99,11 +99,11 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
 
     public UserRepresentation getUserById(String realm, String id) {
         KeycloakSession session = server.getSessionFactory().create();
-        session.getTransaction().begin();
+        session.getTransactionManager().begin();
         try {
             RealmModel realmByName = session.realms().getRealmByName(realm);
             UserRepresentation userRep = ModelToRepresentation.toRepresentation(session.users().getUserById(id, realmByName));
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
             return userRep;
         } finally {
             session.close();
@@ -112,7 +112,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
 
     protected void setupKeycloak() {
         KeycloakSession session = server.getSessionFactory().create();
-        session.getTransaction().begin();
+        session.getTransactionManager().begin();
 
         try {
             RealmManager manager = new RealmManager(session);
@@ -121,7 +121,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
 
             configure(session, manager, adminstrationRealm);
 
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
         } finally {
             session.close();
         }
@@ -129,7 +129,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
 
     public void update(KeycloakRule.KeycloakSetup configurer, String realmId) {
         KeycloakSession session = server.getSessionFactory().create();
-        session.getTransaction().begin();
+        session.getTransactionManager().begin();
 
         try {
             RealmManager manager = new RealmManager(session);
@@ -141,7 +141,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
             configurer.session = session;
             configurer.config(manager, adminstrationRealm, appRealm);
 
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
         } finally {
             session.close();
         }
@@ -221,7 +221,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
     protected void removeTestRealms() {
         KeycloakSession session = server.getSessionFactory().create();
         try {
-            session.getTransaction().begin();
+            session.getTransactionManager().begin();
             RealmManager realmManager = new RealmManager(session);
             for (String realmName : getTestRealms()) {
                 RealmModel realm = realmManager.getRealmByName(realmName);
@@ -229,7 +229,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
                     realmManager.removeRealm(realm);
                 }
             }
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
         } finally {
             session.close();
         }
@@ -248,12 +248,12 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
 
     public KeycloakSession startSession() {
         KeycloakSession session = server.getSessionFactory().create();
-        session.getTransaction().begin();
+        session.getTransactionManager().begin();
         return session;
     }
 
     public void stopSession(KeycloakSession session, boolean commit) {
-        KeycloakTransaction transaction = session.getTransaction();
+        KeycloakTransaction transaction = session.getTransactionManager();
         if (commit && !transaction.getRollbackOnly()) {
             transaction.commit();
         } else {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
index 8e48177..050bcf3 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
@@ -58,7 +58,7 @@ public class KeycloakRule extends AbstractKeycloakRule {
 
     public void configure(KeycloakSetup configurer) {
         KeycloakSession session = server.getSessionFactory().create();
-        session.getTransaction().begin();
+        session.getTransactionManager().begin();
 
         try {
             RealmManager manager = new RealmManager(session);
@@ -70,7 +70,7 @@ public class KeycloakRule extends AbstractKeycloakRule {
             configurer.session = session;
             configurer.config(manager, adminstrationRealm, appRealm);
 
-            session.getTransaction().commit();
+            session.getTransactionManager().commit();
         } finally {
             session.close();
         }