keycloak-uncached

Changes

testsuite/integration-arquillian/tests/other/console_no_users/pom.xml 48(+0 -48)

Details

diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/common/install-patch.bat b/testsuite/integration-arquillian/servers/app-server/jboss/common/install-patch.bat
index ee0ed4f..5c7ce9f 100644
--- a/testsuite/integration-arquillian/servers/app-server/jboss/common/install-patch.bat
+++ b/testsuite/integration-arquillian/servers/app-server/jboss/common/install-patch.bat
@@ -1,9 +1,14 @@
 set NOPAUSE=true
+setlocal EnableDelayedExpansion
 
-for %%a in ("%APP_PATCH_ZIPS:,=" "%") do (
-  call %JBOSS_HOME%\bin\jboss-cli.bat --command="patch apply %%~a"
-
-  if %ERRORLEVEL% neq 0 set ERROR=%ERRORLEVEL%
-)
-
-exit /b %ERROR%
\ No newline at end of file
+ for %%a in (%APP_PATCH_ZIPS%) do (
+   set patch=%%a
+   if "!patch:~0,4!"=="http" (
+      powershell -command "& { iwr %%a -OutFile %cd%\patch.zip }"
+      call %JBOSS_HOME%\bin\jboss-cli.bat --command="patch apply %cd%\patch.zip
+   )  else (
+      call %JBOSS_HOME%\bin\jboss-cli.bat --command="patch apply %%a"
+   )
+   if %ERRORLEVEL% neq 0 set ERROR=%ERRORLEVEL%
+ )
+ exit /b %ERROR%
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/common/install-patch.sh b/testsuite/integration-arquillian/servers/app-server/jboss/common/install-patch.sh
index eb23344..bf091d5 100755
--- a/testsuite/integration-arquillian/servers/app-server/jboss/common/install-patch.sh
+++ b/testsuite/integration-arquillian/servers/app-server/jboss/common/install-patch.sh
@@ -12,6 +12,11 @@ RESULT=0
 patches=$(echo $APP_PATCH_ZIPS | tr "," "\n")
 for patch in $patches
 do
+    if [[ $patch == http* ]];
+    then
+       wget -O ./patch.zip $patch >/dev/null 2>&1
+       patch=./patch.zip
+    fi
     ./jboss-cli.sh --command="patch apply $patch"
     if [ $? -ne 0 ]; then exit 1; fi
 done
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-keycloak.bat b/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-keycloak.bat
index 735b5f2..496c837 100644
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-keycloak.bat
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-keycloak.bat
@@ -1,7 +1,9 @@
 set NOPAUSE=true
 
 call %JBOSS_HOME%\bin\jboss-cli.bat --file=keycloak-install.cli
+call %JBOSS_HOME%\bin\jboss-cli.bat --file=keycloak-install-ha.cli
 
 if %ERRORLEVEL% neq 0 set ERROR=%ERRORLEVEL%
+
 exit /b %ERROR%
 
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-keycloak.sh b/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-keycloak.sh
index af7cebe..8f95237 100755
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-keycloak.sh
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-keycloak.sh
@@ -10,8 +10,8 @@ cd $JBOSS_HOME/bin
 
 RESULT=0
 ./jboss-cli.sh --file=keycloak-install.cli
-if [ $? -ne 0 ]; then RESULT=1; fi
-   exit $RESULT
-fi
+if [ $? -ne 0 ]; then exit 1; fi
+./jboss-cli.sh --file=keycloak-install-ha.cli
+if [ $? -ne 0 ]; then exit 1; fi
 
-exit 1
+exit 0
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-patch.bat b/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-patch.bat
index ee0ed4f..d1c48ae 100644
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-patch.bat
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-patch.bat
@@ -1,9 +1,14 @@
 set NOPAUSE=true
+setlocal EnableDelayedExpansion
 
-for %%a in ("%APP_PATCH_ZIPS:,=" "%") do (
-  call %JBOSS_HOME%\bin\jboss-cli.bat --command="patch apply %%~a"
-
-  if %ERRORLEVEL% neq 0 set ERROR=%ERRORLEVEL%
-)
-
-exit /b %ERROR%
\ No newline at end of file
+ for %%a in (%AUTH_PATCH_ZIPS%) do (
+   set patch=%%a
+   if "!patch:~0,4!"=="http" (
+      powershell -command "& { iwr %%a -OutFile %cd%\patch.zip }"
+      call %JBOSS_HOME%\bin\jboss-cli.bat --command="patch apply %cd%\patch.zip
+   )  else (
+      call %JBOSS_HOME%\bin\jboss-cli.bat --command="patch apply %%a"
+   )
+   if %ERRORLEVEL% neq 0 set ERROR=%ERRORLEVEL%
+ )
+ exit /b %ERROR%
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-patch.sh b/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-patch.sh
index 3d1820b..f1dff06 100755
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-patch.sh
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/install-patch.sh
@@ -12,6 +12,11 @@ RESULT=0
 patches=$(echo $AUTH_PATCH_ZIPS | tr "," "\n")
 for patch in $patches
 do
+    if [[ $patch == http* ]];
+    then
+       wget -O ./patch.zip $patch >/dev/null 2>&1
+       patch=./patch.zip
+    fi
     ./jboss-cli.sh --command="patch apply $patch"
     if [ $? -ne 0 ]; then exit 1; fi
 done
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
index 349eda3..e12af5a 100644
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
@@ -100,6 +100,7 @@
                                             <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
                                         </artifactItem>
                                     </artifactItems>
+                                    <excludes>**/product.conf</excludes>
                                 </configuration>
                             </execution>
                             <execution>
@@ -303,6 +304,7 @@
                                                 <artifactId>${auth.server.overlay.artifactId}</artifactId>
                                                 <version>${auth.server.overlay.version}</version>
                                                 <type>zip</type>
+                                                <overWrite>true</overWrite>
                                                 <outputDirectory>${project.build.directory}/unpacked/${overlaid.container.unpacked.folder.name}</outputDirectory>
                                             </artifactItem>
                                         </artifactItems>
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserMapStorage.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserMapStorage.java
new file mode 100644
index 0000000..b2f80bb
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserMapStorage.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.federation;
+
+import org.keycloak.component.ComponentModel;
+import org.keycloak.credential.CredentialInput;
+import org.keycloak.credential.CredentialInputUpdater;
+import org.keycloak.credential.CredentialInputValidator;
+import org.keycloak.credential.CredentialModel;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.storage.StorageId;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
+import org.keycloak.storage.user.UserLookupProvider;
+import org.keycloak.storage.user.UserRegistrationProvider;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.jboss.logging.Logger;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserMapStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, CredentialInputUpdater, CredentialInputValidator {
+
+    private static final Logger log = Logger.getLogger(UserMapStorage.class);
+    
+    protected Map<String, String> userPasswords;
+    protected ComponentModel model;
+    protected KeycloakSession session;
+
+    public static final AtomicInteger allocations = new AtomicInteger(0);
+    public static final AtomicInteger closings = new AtomicInteger(0);
+    public static final AtomicInteger realmRemovals = new AtomicInteger(0);
+    public static final AtomicInteger groupRemovals = new AtomicInteger(0);
+    public static final AtomicInteger roleRemovals = 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.getExternalId();
+        if (!userPasswords.containsKey(username)) {
+            return null;
+        }
+
+        return createUser(realm, username);
+    }
+
+    private UserModel createUser(RealmModel realm, String username) {
+        return new AbstractUserAdapterFederatedStorage(session, realm, model) {
+            @Override
+            public String getUsername() {
+                return username;
+            }
+
+            @Override
+            public void setUsername(String username) {
+                throw new RuntimeException("Unsupported");
+            }
+
+        };
+    }
+
+    @Override
+    public boolean supportsCredentialType(String credentialType) {
+        return CredentialModel.PASSWORD.equals(credentialType);
+    }
+
+    @Override
+    public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
+        if (!(input instanceof UserCredentialModel)) {
+            return false;
+        }
+        if (input.getType().equals(UserCredentialModel.PASSWORD)) {
+            userPasswords.put(user.getUsername(), ((UserCredentialModel) input).getValue());
+            return true;
+
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
+
+    }
+
+    @Override
+    public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+        return Collections.EMPTY_SET;
+    }
+
+    @Override
+    public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
+        return CredentialModel.PASSWORD.equals(credentialType);
+    }
+
+    @Override
+    public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
+        if (!(input instanceof UserCredentialModel)) {
+            return false;
+        }
+        if (input.getType().equals(UserCredentialModel.PASSWORD)) {
+            String pw = userPasswords.get(user.getUsername());
+            return pw != null && pw.equals(((UserCredentialModel) input).getValue());
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public UserModel getUserByUsername(String username, RealmModel realm) {
+        if (!userPasswords.containsKey(username)) {
+            return null;
+        }
+
+        return createUser(realm, username);
+    }
+
+    @Override
+    public UserModel getUserByEmail(String email, RealmModel realm) {
+        return null;
+    }
+
+    @Override
+    public UserModel addUser(RealmModel realm, String username) {
+        userPasswords.put(username, "");
+        return createUser(realm, username);
+    }
+
+    @Override
+    public boolean removeUser(RealmModel realm, UserModel user) {
+        return userPasswords.remove(user.getUsername()) != null;
+    }
+
+    @Override
+    public void preRemove(RealmModel realm) {
+        log.infof("preRemove: realm=%s", realm.getName());
+        realmRemovals.incrementAndGet();
+    }
+
+    @Override
+    public void preRemove(RealmModel realm, GroupModel group) {
+        log.infof("preRemove: realm=%s, group=%s", realm.getName(), group.getName());
+        groupRemovals.incrementAndGet();
+    }
+
+    @Override
+    public void preRemove(RealmModel realm, RoleModel role) {
+        log.infof("preRemove: realm=%s, role=%s", realm.getName(), role.getName());
+        roleRemovals.incrementAndGet();
+    }
+
+    @Override
+    public void close() {
+        closings.incrementAndGet();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserMapStorageFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserMapStorageFactory.java
new file mode 100644
index 0000000..885c316
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserMapStorageFactory.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.federation;
+
+import java.util.HashMap;
+import org.keycloak.Config;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.storage.UserStorageProviderFactory;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserMapStorageFactory implements UserStorageProviderFactory<UserMapStorage> {
+
+
+    public static final String PROVIDER_ID = "user-password-map-arq";
+
+    protected Map<String, String> userPasswords = new HashMap<>();
+
+    @Override
+    public UserMapStorage create(KeycloakSession session, ComponentModel model) {
+        return new UserMapStorage(session, model, userPasswords);
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java
new file mode 100644
index 0000000..3716aff
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.federation;
+
+import org.keycloak.component.ComponentModel;
+import org.keycloak.credential.CredentialInput;
+import org.keycloak.credential.CredentialInputValidator;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.storage.StorageId;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.storage.adapter.AbstractUserAdapter;
+import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
+import org.keycloak.storage.user.UserLookupProvider;
+import org.keycloak.storage.user.UserQueryProvider;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserPropertyFileStorage implements UserLookupProvider, UserStorageProvider, UserQueryProvider, CredentialInputValidator {
+
+    protected Properties userPasswords;
+    protected ComponentModel model;
+    protected KeycloakSession session;
+    protected boolean federatedStorageEnabled;
+
+    public UserPropertyFileStorage(KeycloakSession session, ComponentModel model, Properties userPasswords) {
+        this.session = session;
+        this.model = model;
+        this.userPasswords = userPasswords;
+        this.federatedStorageEnabled = model.getConfig().containsKey("federatedStorage") && Boolean.valueOf(model.getConfig().getFirst("federatedStorage")).booleanValue();
+    }
+
+
+    @Override
+    public UserModel getUserById(String id, RealmModel realm) {
+        StorageId storageId = new StorageId(id);
+        final String username = storageId.getExternalId();
+        if (!userPasswords.containsKey(username)) return null;
+
+        return createUser(realm, username);
+    }
+
+    private UserModel createUser(RealmModel realm, String username) {
+        if (federatedStorageEnabled) {
+            return new AbstractUserAdapterFederatedStorage(session, realm,  model) {
+                @Override
+                public String getUsername() {
+                    return username;
+                }
+
+                @Override
+                public void setUsername(String username) {
+                    throw new RuntimeException("Unsupported");
+                }
+            };
+        } else {
+            return new AbstractUserAdapter(session, realm, model) {
+                @Override
+                public String getUsername() {
+                    return username;
+                }
+            };
+        }
+    }
+
+    @Override
+    public UserModel getUserByUsername(String username, RealmModel realm) {
+        if (!userPasswords.containsKey(username)) return null;
+
+        return createUser(realm, username);
+    }
+
+    @Override
+    public UserModel getUserByEmail(String email, RealmModel realm) {
+        return null;
+    }
+
+    @Override
+    public void preRemove(RealmModel realm) {
+
+    }
+
+    @Override
+    public void preRemove(RealmModel realm, GroupModel group) {
+
+    }
+
+    @Override
+    public void preRemove(RealmModel realm, RoleModel role) {
+
+    }
+
+    @Override
+    public boolean supportsCredentialType(String credentialType) {
+        return credentialType.equals(UserCredentialModel.PASSWORD);
+    }
+
+    @Override
+    public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
+        return credentialType.equals(UserCredentialModel.PASSWORD) && userPasswords.get(user.getUsername()) != null;
+    }
+
+    @Override
+    public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
+        if (!(input instanceof UserCredentialModel)) return false;
+        if (input.getType().equals(UserCredentialModel.PASSWORD)) {
+            String pw = (String)userPasswords.get(user.getUsername());
+            return pw != null && pw.equals( ((UserCredentialModel)input).getValue());
+        } else {
+            return false;
+        }
+    }
+
+
+    @Override
+    public int getUsersCount(RealmModel realm) {
+        return userPasswords.size();
+    }
+
+    @Override
+    public List<UserModel> getUsers(RealmModel realm) {
+        List<UserModel> users = new LinkedList<>();
+        for (Object username : userPasswords.keySet()) {
+            users.add(createUser(realm, (String)username));
+        }
+        return users;
+    }
+
+    @Override
+    public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm) {
+        return Collections.EMPTY_LIST;
+    }
+
+    @Override
+    public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
+        if (maxResults == 0) return Collections.EMPTY_LIST;
+        List<UserModel> users = new LinkedList<>();
+        int count = 0;
+        for (Object un : userPasswords.keySet()) {
+            if (count++ < firstResult) continue;
+            String username = (String)un;
+            users.add(createUser(realm, username));
+            if (users.size() + 1 > maxResults) break;
+        }
+        return users;
+    }
+
+    @Override
+    public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
+        if (maxResults == 0) return Collections.EMPTY_LIST;
+        List<UserModel> users = new LinkedList<>();
+        int count = 0;
+        for (Object un : userPasswords.keySet()) {
+            String username = (String)un;
+            if (username.contains(search)) {
+                if (count++ < firstResult) {
+                    continue;
+                }
+                users.add(createUser(realm, username));
+                if (users.size() + 1 > maxResults) break;
+            }
+        }
+        return users;
+    }
+
+    @Override
+    public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
+        if (attributes.size() != 1) return Collections.EMPTY_LIST;
+        String username = attributes.get(UserModel.USERNAME);
+        if (username == null) return Collections.EMPTY_LIST;
+        return searchForUser(username, realm, firstResult, maxResults);
+    }
+
+    @Override
+    public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
+        return Collections.EMPTY_LIST;
+    }
+
+    @Override
+    public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
+        return Collections.EMPTY_LIST;
+    }
+
+    @Override
+    public List<UserModel> searchForUser(String search, RealmModel realm) {
+        return getUsers(realm, 0, Integer.MAX_VALUE - 1);
+    }
+
+    @Override
+    public List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) {
+        return Collections.EMPTY_LIST;
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorageFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorageFactory.java
new file mode 100644
index 0000000..7b3f375
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorageFactory.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.federation;
+
+import java.io.File;
+import java.io.FileInputStream;
+import org.keycloak.Config;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.storage.UserStorageProviderFactory;
+import org.keycloak.storage.UserStorageProviderModel;
+import org.keycloak.storage.user.ImportSynchronization;
+import org.keycloak.storage.user.SynchronizationResult;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+import org.keycloak.common.util.EnvUtil;
+import org.keycloak.component.ComponentValidationException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.ProviderConfigurationBuilder;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserPropertyFileStorageFactory implements UserStorageProviderFactory<UserPropertyFileStorage>, ImportSynchronization {
+
+    public static final String PROVIDER_ID = "user-password-props-arq";
+    public static final String PROPERTY_FILE = "propertyFile";
+
+    public static final String VALIDATION_PROP_FILE_NOT_CONFIGURED = "user property file is not configured";
+    public static final String VALIDATION_PROP_FILE_DOESNT_EXIST = "user property file does not exist";
+
+    protected static final List<ProviderConfigProperty> CONFIG_PROPERTIES;
+
+    static {
+        CONFIG_PROPERTIES = ProviderConfigurationBuilder.create()
+                .property().name(PROPERTY_FILE)
+                .type(ProviderConfigProperty.STRING_TYPE)
+                .label("Property File")
+                .helpText("File that contains name value pairs")
+                .defaultValue(null)
+                .add()
+                .property().name("federatedStorage")
+                .type(ProviderConfigProperty.BOOLEAN_TYPE)
+                .label("User Federated Storage")
+                .helpText("User Federated Storage")
+                .defaultValue(null)
+                .add()
+                .build();
+    }
+
+    @Override
+    public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel config) throws ComponentValidationException {
+        String fp = config.getConfig().getFirst(PROPERTY_FILE);
+        if (fp == null) {
+            throw new ComponentValidationException(VALIDATION_PROP_FILE_NOT_CONFIGURED);
+        }
+        fp = EnvUtil.replace(fp);
+        File file = new File(fp);
+        if (!file.exists()) {
+            throw new ComponentValidationException(VALIDATION_PROP_FILE_DOESNT_EXIST);
+        }
+    }
+
+    @Override
+    public UserPropertyFileStorage create(KeycloakSession session, ComponentModel model) {
+        String path = model.getConfig().getFirst(PROPERTY_FILE);
+        path = EnvUtil.replace(path);
+
+        Properties props = new Properties();
+        try (InputStream is = new FileInputStream(path)) {
+            props.load(is);
+            is.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        return new UserPropertyFileStorage(session, model, props);
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return CONFIG_PROPERTIES;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public SynchronizationResult sync(KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model) {
+        return SynchronizationResult.ignored();
+    }
+
+    @Override
+    public SynchronizationResult syncSince(Date lastSync, KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model) {
+        return SynchronizationResult.ignored();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
index a97dd1e..a1a53ab 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
@@ -1 +1,3 @@
-org.keycloak.testsuite.federation.DummyUserFederationProviderFactory
\ No newline at end of file
+org.keycloak.testsuite.federation.DummyUserFederationProviderFactory
+org.keycloak.testsuite.federation.UserMapStorageFactory
+org.keycloak.testsuite.federation.UserPropertyFileStorageFactory
diff --git a/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowDeployerHelper.java b/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowDeployerHelper.java
index bd632c3..bcb726f 100644
--- a/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowDeployerHelper.java
+++ b/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowDeployerHelper.java
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.keycloak.testsuite.arquillian.undertow;
 
 import java.io.IOException;
@@ -83,7 +82,6 @@ class UndertowDeployerHelper {
         }
     }
 
-
     private ResourceManager getResourceManager(final String appServerRoot, final WebArchive archive) throws IOException {
         return new ResourceManager() {
 
@@ -139,7 +137,6 @@ class UndertowDeployerHelper {
                 throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported();
             }
 
-
             @Override
             public void close() throws IOException {
                 // TODO: Should close open streams?
@@ -148,7 +145,6 @@ class UndertowDeployerHelper {
         };
     }
 
-
     private Document loadXML(InputStream is) {
         try {
             DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
@@ -159,7 +155,6 @@ class UndertowDeployerHelper {
         }
     }
 
-
     private void addAnnotatedServlets(DeploymentInfo di, Archive<?> archive) {
         Map<ArchivePath, Node> classNodes = archive.getContent((ArchivePath path) -> {
 
@@ -170,25 +165,26 @@ class UndertowDeployerHelper {
 
         for (Map.Entry<ArchivePath, Node> entry : classNodes.entrySet()) {
             Node n = entry.getValue();
-            ClassAsset classAsset = (ClassAsset) n.getAsset();
-            Class<?> clazz = classAsset.getSource();
-
-            WebServlet annotation = clazz.getAnnotation(WebServlet.class);
-            if (annotation != null) {
-                ServletInfo undertowServlet = new ServletInfo(clazz.getSimpleName(), (Class<? extends Servlet>) clazz);
-
-                String[] mappings = annotation.value();
-                if (mappings != null) {
-                    for (String urlPattern : mappings) {
-                        undertowServlet.addMapping(urlPattern);
+            if (n.getAsset() instanceof ClassAsset) {
+                ClassAsset classAsset = (ClassAsset) n.getAsset();
+                Class<?> clazz = classAsset.getSource();
+
+                WebServlet annotation = clazz.getAnnotation(WebServlet.class);
+                if (annotation != null) {
+                    ServletInfo undertowServlet = new ServletInfo(clazz.getSimpleName(), (Class<? extends Servlet>) clazz);
+
+                    String[] mappings = annotation.value();
+                    if (mappings != null) {
+                        for (String urlPattern : mappings) {
+                            undertowServlet.addMapping(urlPattern);
+                        }
                     }
-                }
 
-                di.addServlet(undertowServlet);
+                    di.addServlet(undertowServlet);
+                }
             }
         }
 
     }
 
-
 }
diff --git a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
index f34878a..0f1d3f2 100755
--- a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
+++ b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
@@ -194,9 +194,6 @@ TimeSkew:
     function sendBearerToKeycloak() {
         var url = 'http://localhost:8180/auth/admin/realms/example/roles';
         if (window.location.href.indexOf("8643") > -1) {
-            url = url.replace("8180","8543");
-            url = url.replace("http","https");
-        if (window.location.href.indexOf("8543") > -1) {
             url = url.replace("8180", "8543");
             url = url.replace("http", "https");
         }
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index 429c6dc..5b6cc92 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -1,20 +1,20 @@
 <?xml version="1.0"?>
 <!--
-  ~ 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.
-  -->
+~ 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 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
index 570c9e8..7fbab6a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
@@ -68,11 +68,16 @@ public abstract class AbstractAuthTest extends AbstractKeycloakTest {
         testRealms.add(testRealmRep);
     }
 
-    @Before
-    public void beforeAuthTest() {
+    @Override
+    public void setDefaultPageUriParameters() {
+        super.setDefaultPageUriParameters();
+        testRealmPage.setAuthRealm(TEST);
         testRealmLoginPage.setAuthRealm(testRealmPage);
         testRealmAccountPage.setAuthRealm(testRealmPage);
+    }
 
+    @Before
+    public void beforeAuthTest() {
         testUser = createUserRepresentation("test", "test@email.test", "test", "user", true);
         setPasswordFor(testUser, PASSWORD);
 
@@ -82,6 +87,7 @@ public abstract class AbstractAuthTest extends AbstractKeycloakTest {
         deleteAllCookiesForTestRealm();
     }
 
+
     public void createTestUserWithAdminClient() {
         log.debug("creating test user");
         String id = createUserAndResetPasswordWithAdminClient(testRealmResource(), testUser, PASSWORD);
@@ -116,4 +122,4 @@ public abstract class AbstractAuthTest extends AbstractKeycloakTest {
         return adminClient.realm(testRealmPage.getAuthRealm());
     }
 
-}
\ No newline at end of file
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
index cf50cce..6ce3e21 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
@@ -445,6 +445,7 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
     }
 
     @Test
+    @Ignore
     public void testVersion() {
         Client client = ClientBuilder.newClient();
         WebTarget target = client.target(authServerPage.createUriBuilder()).path("version");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java
index 97edc00..bf366cf 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java
@@ -53,8 +53,8 @@ public class ApiUtil {
         URI location = response.getLocation();
         if (!response.getStatusInfo().equals(Status.CREATED)) {
             StatusType statusInfo = response.getStatusInfo();
-            throw new WebApplicationException("Create method returned status " +
-                    statusInfo.getReasonPhrase() + " (Code: " + statusInfo.getStatusCode() + "); expected status: Created (201)", response);
+            throw new WebApplicationException("Create method returned status "
+                    + statusInfo.getReasonPhrase() + " (Code: " + statusInfo.getStatusCode() + "); expected status: Created (201)", response);
         }
         if (location == null) {
             return null;
@@ -118,14 +118,16 @@ public class ApiUtil {
 
     public static UserRepresentation findUserByUsername(RealmResource realm, String username) {
         UserRepresentation user = null;
-        List<UserRepresentation> ur = realm.users().search(username, null, null);
+        List<UserRepresentation> ur = realm.users().search(username, null, null, null, 0, Integer.MAX_VALUE);
         if (ur.size() == 1) {
             user = ur.get(0);
         }
 
         if (ur.size() > 1) { // try to be more specific
             for (UserRepresentation rep : ur) {
-                if (rep.getUsername().equalsIgnoreCase(username)) return rep;
+                if (rep.getUsername().equalsIgnoreCase(username)) {
+                    return rep;
+                }
             }
         }
 
@@ -157,6 +159,21 @@ public class ApiUtil {
         userResource.resetPassword(newCredential);
     }
 
+    public static void assignRealmRoles(RealmResource realm, String userId, String... roles) {
+        String realmName = realm.toRepresentation().getRealm();
+
+        List<RoleRepresentation> roleRepresentations = new ArrayList<>();
+        for (String roleName : roles) {
+            RoleRepresentation role = realm.roles().get(roleName).toRepresentation();
+            roleRepresentations.add(role);
+        }
+
+        UserResource userResource = realm.users().get(userId);
+        log.info("assigning roles " + Arrays.toString(roles) + " to user: \""
+                + userResource.toRepresentation().getUsername() + "\" in realm: \"" + realmName + "\"");
+        userResource.roles().realmLevel().add(roleRepresentations);
+    }
+
     public static void assignClientRoles(RealmResource realm, String userId, String clientName, String... roles) {
         String realmName = realm.toRepresentation().getRealm();
         String clientId = "";
@@ -176,7 +193,7 @@ public class ApiUtil {
             }
 
             UserResource userResource = realm.users().get(userId);
-            log.debug("assigning role: " + Arrays.toString(roles) + " to user: \""
+            log.info("assigning role: " + Arrays.toString(roles) + " to user: \""
                     + userResource.toRepresentation().getUsername() + "\" of client: \""
                     + clientName + "\" in realm: \"" + realmName + "\"");
             userResource.roles().clientLevel(clientId).add(roleRepresentations);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java
index eed784b..c247009 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java
@@ -36,6 +36,7 @@ import org.keycloak.testsuite.util.RealmBuilder;
 
 import javax.ws.rs.core.Response;
 import java.util.List;
+import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
 
 /**
  *
@@ -46,6 +47,14 @@ public abstract class AbstractClientTest extends AbstractAuthTest {
     @Rule
     public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this);
 
+    @Override
+    public void setDefaultPageUriParameters() {
+        super.setDefaultPageUriParameters();
+        testRealmPage.setAuthRealm(MASTER);
+        testRealmLoginPage.setAuthRealm(testRealmPage);
+        testRealmAccountPage.setAuthRealm(testRealmPage);
+    }    
+    
     @Before
     public void setupAdminEvents() {
         RealmRepresentation realm = testRealmResource().toRepresentation();
@@ -66,7 +75,7 @@ public abstract class AbstractClientTest extends AbstractAuthTest {
     }
 
     protected String getRealmId() {
-        return "master";
+        return MASTER;
     }
 
     // returns UserRepresentation retrieved from server, with all fields, including id
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java
index 03df30e..9cb3c32 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java
@@ -14,7 +14,6 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package org.keycloak.testsuite.admin.event;
 
 import org.junit.Before;
@@ -23,6 +22,7 @@ import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
 import org.keycloak.testsuite.AbstractAuthTest;
 
 import java.util.Collections;
+import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
 
 /**
  *
@@ -32,6 +32,14 @@ public abstract class AbstractEventTest extends AbstractAuthTest {
 
     protected RealmEventsConfigRepresentation configRep;
 
+    @Override
+    public void setDefaultPageUriParameters() {
+        super.setDefaultPageUriParameters();
+        testRealmPage.setAuthRealm(MASTER);
+        testRealmLoginPage.setAuthRealm(testRealmPage);
+        testRealmAccountPage.setAuthRealm(testRealmPage);
+    }
+
     @Before
     public void setConfigRep() {
         RealmResource testRsc = testRealmResource();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java
index 6125ab3..cdd8856 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java
@@ -339,9 +339,9 @@ public class PartialImportTest extends AbstractAuthTest {
     @Test
     public void testAddUsersWithDuplicateEmailsAllowed() {
         
-        RealmRepresentation realmRep = new RealmRepresentation();
+        RealmRepresentation realmRep = testRealmResource().toRepresentation();
         realmRep.setDuplicateEmailsAllowed(true);
-        adminClient.realm(realmId).update(realmRep);
+        testRealmResource().update(realmRep);
                 
         assertAdminEvents.clear();
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/LegacyImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/LegacyImportTest.java
index 3dcffcd..b5549e1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/LegacyImportTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/LegacyImportTest.java
@@ -42,6 +42,7 @@ import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.exportimport.Strategy;
 import static org.keycloak.testsuite.Assert.assertNames;
 import static org.keycloak.testsuite.migration.MigrationTest.MIGRATION;
+import static org.keycloak.testsuite.migration.MigrationTest.MIGRATION2;
 
 /**
  * Test importing JSON files exported from previous adminClient versions
@@ -85,6 +86,7 @@ public class LegacyImportTest extends AbstractExportImportTest {
             assertNames(imported.groups().groups(), "migration-test-group");
         } finally {
             removeRealm(MIGRATION);
+            removeRealm(MIGRATION2);
         }
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ComponentExportImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ComponentExportImportTest.java
new file mode 100644
index 0000000..cc57b8e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ComponentExportImportTest.java
@@ -0,0 +1,139 @@
+package org.keycloak.testsuite.federation.storage;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+import javax.ws.rs.NotFoundException;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import static org.junit.Assert.fail;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.exportimport.ExportImportConfig;
+import org.keycloak.exportimport.ExportImportManager;
+import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
+import org.keycloak.representations.idm.ComponentRepresentation;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.testsuite.AbstractAuthTest;
+import org.keycloak.testsuite.admin.ApiUtil;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+import org.keycloak.testsuite.federation.UserMapStorageFactory;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ComponentExportImportTest extends AbstractAuthTest implements Serializable {
+
+    private File exportFile;
+
+    @Deployment
+    public static WebArchive deploy() {
+        return RunOnServerDeployment.create(ComponentExportImportTest.class, AbstractAuthTest.class, RealmResource.class)
+                .addPackages(true, "org.keycloak.testsuite");
+    }
+
+    @Before
+    public void setDirs() {
+        exportFile = new File (new File(System.getProperty("auth.server.config.dir", "target")), "singleFile-full.json");
+        log.infof("Export file: %s", exportFile);
+    }
+
+    public void clearExportImportProperties() {
+        // Clear export/import properties after test
+        Properties systemProps = System.getProperties();
+        Set<String> propsToRemove = new HashSet<>();
+
+        for (Object key : systemProps.keySet()) {
+            if (key.toString().startsWith(ExportImportConfig.PREFIX)) {
+                propsToRemove.add(key.toString());
+            }
+        }
+
+        for (String propToRemove : propsToRemove) {
+            systemProps.remove(propToRemove);
+        }
+    }
+
+    protected String addComponent(ComponentRepresentation component) {
+        return ApiUtil.getCreatedId(testRealmResource().components().add(component));
+    }
+
+    @Test
+    @Ignore
+    public void testSingleFile() {
+        clearExportImportProperties();
+
+        String realmId = testRealmResource().toRepresentation().getId();
+        String realmName = testRealmResource().toRepresentation().getRealm();
+
+        ComponentRepresentation parentComponent = new ComponentRepresentation();
+        parentComponent.setParentId(realmId);
+        parentComponent.setName("parent");
+        parentComponent.setSubType("subtype");
+        parentComponent.setProviderId(UserMapStorageFactory.PROVIDER_ID);
+        parentComponent.setProviderType(UserStorageProvider.class.getName());
+        parentComponent.setConfig(new MultivaluedHashMap<>());
+        parentComponent.getConfig().putSingle("priority", Integer.toString(0));
+        parentComponent.getConfig().putSingle("attr", "value");
+        String parentComponentId = addComponent(parentComponent);
+
+        ComponentRepresentation subcomponent = new ComponentRepresentation();
+        subcomponent.setParentId(parentComponentId);
+        subcomponent.setName("child");
+        subcomponent.setSubType("subtype2");
+        subcomponent.setProviderId(UserMapStorageFactory.PROVIDER_ID);
+        subcomponent.setProviderType(UserStorageProvider.class.getName());
+        subcomponent.setConfig(new MultivaluedHashMap<>());
+        subcomponent.getConfig().putSingle("priority", Integer.toString(0));
+        subcomponent.getConfig().putSingle("attr", "value2");
+        String subcomponentId = addComponent(subcomponent);
+
+        // export 
+        testingClient.server().run(session -> {
+            ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+            ExportImportConfig.setFile(exportFile.getAbsolutePath());
+            ExportImportConfig.setRealmName(realmName);
+            ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
+            new ExportImportManager(session).runExport();
+        });
+
+        // import 
+        testingClient.server().run(session -> {
+            Assert.assertNull(session.realms().getRealmByName(TEST));
+            ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
+            new ExportImportManager(session).runImport();
+            Assert.assertNotNull(session.realms().getRealmByName(TEST));
+        });
+
+        try {
+            parentComponent = testRealmResource().components().component(parentComponentId).toRepresentation();
+            subcomponent = testRealmResource().components().component(subcomponentId).toRepresentation();
+        } catch (NotFoundException nfe) {
+            fail("Components not found after import.");
+        }
+
+        Assert.assertEquals(parentComponent.getParentId(), realmId);
+        Assert.assertEquals(parentComponent.getName(), "parent");
+        Assert.assertEquals(parentComponent.getSubType(), "subtype");
+        Assert.assertEquals(parentComponent.getProviderId(), UserMapStorageFactory.PROVIDER_ID);
+        Assert.assertEquals(parentComponent.getProviderType(), UserStorageProvider.class.getName());
+        Assert.assertEquals(parentComponent.getConfig().getFirst("attr"), "value");
+
+        Assert.assertEquals(subcomponent.getParentId(), realmId);
+        Assert.assertEquals(subcomponent.getName(), "child");
+        Assert.assertEquals(subcomponent.getSubType(), "subtype2");
+        Assert.assertEquals(subcomponent.getProviderId(), UserMapStorageFactory.PROVIDER_ID);
+        Assert.assertEquals(subcomponent.getProviderType(), UserStorageProvider.class.getName());
+        Assert.assertEquals(subcomponent.getConfig().getFirst("attr"), "value2");
+
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
new file mode 100644
index 0000000..a5f00a7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
@@ -0,0 +1,585 @@
+package org.keycloak.testsuite.federation.storage;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import static java.util.Calendar.DAY_OF_WEEK;
+import static java.util.Calendar.HOUR_OF_DAY;
+import static java.util.Calendar.MINUTE;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.NotFoundException;
+import org.apache.commons.io.FileUtils;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PROFILE;
+import org.keycloak.models.cache.CachedUserModel;
+import org.keycloak.representations.idm.ComponentRepresentation;
+import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.storage.UserStorageProvider;
+import static org.keycloak.storage.UserStorageProviderModel.CACHE_POLICY;
+import org.keycloak.storage.UserStorageProviderModel.CachePolicy;
+import static org.keycloak.storage.UserStorageProviderModel.EVICTION_DAY;
+import static org.keycloak.storage.UserStorageProviderModel.EVICTION_HOUR;
+import static org.keycloak.storage.UserStorageProviderModel.EVICTION_MINUTE;
+import static org.keycloak.storage.UserStorageProviderModel.MAX_LIFESPAN;
+import org.keycloak.testsuite.AbstractAuthTest;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.federation.UserMapStorage;
+import org.keycloak.testsuite.federation.UserMapStorageFactory;
+import org.keycloak.testsuite.federation.UserPropertyFileStorageFactory;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserStorageTest extends AbstractAuthTest {
+
+    private String memProviderId;
+    private String propProviderROId;
+    private String propProviderRWId;
+
+    private static final File CONFIG_DIR = new File(System.getProperty("auth.server.config.dir", ""));
+
+    @Before
+    public void addProvidersBeforeTest() throws URISyntaxException, IOException {
+        ComponentRepresentation memProvider = new ComponentRepresentation();
+        memProvider.setName("memory");
+        memProvider.setProviderId(UserMapStorageFactory.PROVIDER_ID);
+        memProvider.setProviderType(UserStorageProvider.class.getName());
+        memProvider.setConfig(new MultivaluedHashMap<>());
+        memProvider.getConfig().putSingle("priority", Integer.toString(0));
+
+        memProviderId = addComponent(memProvider);
+
+        // copy files used by the following RO/RW user providers
+        File stResDir = new File(getClass().getResource("/storage-test").toURI());
+        if (stResDir.exists() && stResDir.isDirectory() && CONFIG_DIR.exists() && CONFIG_DIR.isDirectory()) {
+            for (File f : stResDir.listFiles()) {
+                log.infof("Copying %s to %s", f.getName(), CONFIG_DIR.getAbsolutePath());
+                FileUtils.copyFileToDirectory(f, CONFIG_DIR);
+            }
+        } else {
+            throw new RuntimeException("Property `auth.server.config.dir` must be set to run UserStorageTests.");
+        }
+
+        ComponentRepresentation propProviderRO = new ComponentRepresentation();
+        propProviderRO.setName("read-only-user-props");
+        propProviderRO.setProviderId(UserPropertyFileStorageFactory.PROVIDER_ID);
+        propProviderRO.setProviderType(UserStorageProvider.class.getName());
+        propProviderRO.setConfig(new MultivaluedHashMap<>());
+        propProviderRO.getConfig().putSingle("priority", Integer.toString(1));
+        propProviderRO.getConfig().putSingle("propertyFile",
+                CONFIG_DIR.getAbsolutePath() + File.separator + "read-only-user-password.properties");
+
+        propProviderROId = addComponent(propProviderRO);
+
+        propProviderRWId = addComponent(newPropProviderRW());
+
+    }
+
+    protected ComponentRepresentation newPropProviderRW() {
+        ComponentRepresentation propProviderRW = new ComponentRepresentation();
+        propProviderRW.setName("user-props");
+        propProviderRW.setProviderId(UserPropertyFileStorageFactory.PROVIDER_ID);
+        propProviderRW.setProviderType(UserStorageProvider.class.getName());
+        propProviderRW.setConfig(new MultivaluedHashMap<>());
+        propProviderRW.getConfig().putSingle("priority", Integer.toString(2));
+        propProviderRW.getConfig().putSingle("propertyFile", CONFIG_DIR.getAbsolutePath() + File.separator + "user-password.properties");
+        propProviderRW.getConfig().putSingle("federatedStorage", "true");
+        return propProviderRW;
+    }
+
+    protected String addComponent(ComponentRepresentation component) {
+        return ApiUtil.getCreatedId(testRealmResource().components().add(component));
+    }
+
+    private void loginSuccessAndLogout(String username, String password) {
+        testRealmAccountPage.navigateTo();
+        testRealmLoginPage.form().login(username, password);
+        assertCurrentUrlStartsWith(testRealmAccountPage);
+        testRealmAccountPage.logOut();
+    }
+
+    public void loginBadPassword(String username) {
+        testRealmAccountPage.navigateTo();
+        testRealmLoginPage.form().login(username, "badpassword");
+        assertCurrentUrlDoesntStartWith(testRealmAccountPage);
+    }
+
+//    @Test
+    public void listComponents() {
+        log.info("COMPONENTS:");
+        testRealmResource().components().query().forEach((c) -> {
+            log.infof("%s - %s - %s", c.getId(), c.getProviderType(), c.getName());
+        });
+    }
+
+    @Test
+    public void testLoginSuccess() {
+        loginSuccessAndLogout("tbrady", "goat");
+        loginSuccessAndLogout("thor", "hammer");
+        loginBadPassword("tbrady");
+    }
+
+    @Test
+    public void testUpdate() {
+        UserRepresentation thor = ApiUtil.findUserByUsername(testRealmResource(), "thor");
+
+        // update entity
+        thor.setFirstName("Stian");
+        thor.setLastName("Thorgersen");
+        thor.setEmailVerified(true);
+        long thorCreated = System.currentTimeMillis() - 100;
+        thor.setCreatedTimestamp(thorCreated);
+        thor.setEmail("thor@hammer.com");
+        thor.setAttributes(new HashMap<>());
+        thor.getAttributes().put("test-attribute", Arrays.asList("value"));
+        thor.setRequiredActions(new ArrayList<>());
+        thor.getRequiredActions().add(UPDATE_PROFILE.name());
+        testRealmResource().users().get(thor.getId()).update(thor);
+
+        // check entity
+        thor = ApiUtil.findUserByUsername(testRealmResource(), "thor");
+        Assert.assertEquals("Stian", thor.getFirstName());
+        Assert.assertEquals("Thorgersen", thor.getLastName());
+        Assert.assertEquals("thor@hammer.com", thor.getEmail());
+        Assert.assertTrue(thor.getAttributes().containsKey("test-attribute"));
+        Assert.assertEquals(1, thor.getAttributes().get("test-attribute").size());
+        Assert.assertEquals("value", thor.getAttributes().get("test-attribute").get(0));
+        Assert.assertTrue(thor.isEmailVerified());
+
+        // update group
+        GroupRepresentation g = new GroupRepresentation();
+        g.setName("my-group");
+        String gid = ApiUtil.getCreatedId(testRealmResource().groups().add(g));
+
+        testRealmResource().users().get(thor.getId()).joinGroup(gid);
+
+        // check group
+        boolean foundGroup = false;
+        for (GroupRepresentation ug : testRealmResource().users().get(thor.getId()).groups()) {
+            if (ug.getId().equals(gid)) {
+                foundGroup = true;
+            }
+        }
+        Assert.assertTrue(foundGroup);
+
+        // check required actions
+        assertTrue(thor.getRequiredActions().contains(UPDATE_PROFILE.name()));
+        // remove req. actions
+        thor.getRequiredActions().remove(UPDATE_PROFILE.name());
+        testRealmResource().users().get(thor.getId()).update(thor);
+
+        // change pass
+        ApiUtil.resetUserPassword(testRealmResource().users().get(thor.getId()), "lightning", false);
+        loginSuccessAndLogout("thor", "lightning");
+
+        // update role
+        RoleRepresentation r = new RoleRepresentation("foo-role", "foo role", false);
+        testRealmResource().roles().create(r);
+        ApiUtil.assignRealmRoles(testRealmResource(), thor.getId(), "foo-role");
+
+        // check role
+        boolean foundRole = false;
+        for (RoleRepresentation rr : user(thor.getId()).roles().getAll().getRealmMappings()) {
+            if ("foo-role".equals(rr.getName())) {
+                foundRole = true;
+                break;
+            }
+        }
+        assertTrue(foundRole);
+
+        // test removal of provider
+        testRealmResource().components().component(propProviderRWId).remove();
+        propProviderRWId = addComponent(newPropProviderRW());
+        loginSuccessAndLogout("thor", "hammer");
+
+        thor = ApiUtil.findUserByUsername(testRealmResource(), "thor");
+
+        Assert.assertNull(thor.getFirstName());
+        Assert.assertNull(thor.getLastName());
+        Assert.assertNull(thor.getEmail());
+        Assert.assertNull(thor.getAttributes());
+        Assert.assertFalse(thor.isEmailVerified());
+
+        foundGroup = false;
+        for (GroupRepresentation ug : testRealmResource().users().get(thor.getId()).groups()) {
+            if (ug.getId().equals(gid)) {
+                foundGroup = true;
+            }
+        }
+        Assert.assertFalse(foundGroup);
+
+        foundRole = false;
+        for (RoleRepresentation rr : user(thor.getId()).roles().getAll().getRealmMappings()) {
+            if ("foo-role".equals(rr.getName())) {
+                foundRole = true;
+                break;
+            }
+        }
+        assertFalse(foundRole);
+    }
+
+    public UserResource user(String userId) {
+        return testRealmResource().users().get(userId);
+    }
+
+    @Test
+    public void testRegistration() {
+        UserRepresentation memuser = new UserRepresentation();
+        memuser.setUsername("memuser");
+        String uid = ApiUtil.createUserAndResetPasswordWithAdminClient(testRealmResource(), memuser, "password");
+        loginSuccessAndLogout("memuser", "password");
+        loginSuccessAndLogout("memuser", "password");
+        loginSuccessAndLogout("memuser", "password");
+
+        memuser = user(uid).toRepresentation();
+        assertNotNull(memuser);
+        assertNotNull(memuser.getOrigin());
+        ComponentRepresentation origin = testRealmResource().components().component(memuser.getOrigin()).toRepresentation();
+        Assert.assertEquals("memory", origin.getName());
+
+        testRealmResource().users().get(memuser.getId()).remove();
+        try {
+            user(uid).toRepresentation(); // provider doesn't implement UserQueryProvider --> have to lookup by uid
+            fail("`memuser` wasn't removed");
+        } catch (NotFoundException nfe) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testQuery() {
+        Set<UserRepresentation> queried = new HashSet<>();
+        int first = 0;
+        while (queried.size() < 8) {
+            List<UserRepresentation> results = testRealmResource().users().search("", first, 3);
+            log.debugf("first=%s, results: %s", first, results.size());
+            if (results.isEmpty()) {
+                break;
+            }
+            first += results.size();
+            queried.addAll(results);
+        }
+        Set<String> usernames = new HashSet<>();
+        for (UserRepresentation user : queried) {
+            usernames.add(user.getUsername());
+            log.info(user.getUsername());
+        }
+        Assert.assertEquals(8, queried.size());
+        Assert.assertTrue(usernames.contains("thor"));
+        Assert.assertTrue(usernames.contains("zeus"));
+        Assert.assertTrue(usernames.contains("apollo"));
+        Assert.assertTrue(usernames.contains("perseus"));
+        Assert.assertTrue(usernames.contains("tbrady"));
+        Assert.assertTrue(usernames.contains("rob"));
+        Assert.assertTrue(usernames.contains("jules"));
+        Assert.assertTrue(usernames.contains("danny"));
+
+        // test searchForUser
+        List<UserRepresentation> users = testRealmResource().users().search("tbrady", 0, Integer.MAX_VALUE);
+        Assert.assertTrue(users.size() == 1);
+        Assert.assertTrue(users.get(0).getUsername().equals("tbrady"));
+
+        // test getGroupMembers()
+        GroupRepresentation g = new GroupRepresentation();
+        g.setName("gods");
+        String gid = ApiUtil.getCreatedId(testRealmResource().groups().add(g));
+
+        UserRepresentation user = ApiUtil.findUserByUsername(testRealmResource(), "apollo");
+        testRealmResource().users().get(user.getId()).joinGroup(gid);
+        user = ApiUtil.findUserByUsername(testRealmResource(), "zeus");
+        testRealmResource().users().get(user.getId()).joinGroup(gid);
+        user = ApiUtil.findUserByUsername(testRealmResource(), "thor");
+        testRealmResource().users().get(user.getId()).joinGroup(gid);
+        queried.clear();
+        usernames.clear();
+
+        first = 0;
+        while (queried.size() < 8) {
+            List<UserRepresentation> results = testRealmResource().groups().group(gid).members(first, 1);
+            log.debugf("first=%s, results: %s", first, results.size());
+            if (results.isEmpty()) {
+                break;
+            }
+            first += results.size();
+            queried.addAll(results);
+        }
+        for (UserRepresentation u : queried) {
+            usernames.add(u.getUsername());
+            log.info(u.getUsername());
+        }
+        Assert.assertEquals(3, queried.size());
+        Assert.assertTrue(usernames.contains("apollo"));
+        Assert.assertTrue(usernames.contains("zeus"));
+        Assert.assertTrue(usernames.contains("thor"));
+
+        // search by single attribute
+        // FIXME - no equivalent for model in REST
+    }
+
+    @Deployment
+    public static WebArchive deploy() {
+        return RunOnServerDeployment.create(UserResource.class)
+                .addPackages(true, "org.keycloak.testsuite");
+    }
+
+    @Test
+    public void testDailyEviction() {
+        ApiUtil.findUserByUsername(testRealmResource(), "thor");
+
+        // set eviction to 1 hour from now
+        Calendar eviction = Calendar.getInstance();
+        eviction.add(Calendar.HOUR, 1);
+        ComponentRepresentation propProviderRW = testRealmResource().components().component(propProviderRWId).toRepresentation();
+        propProviderRW.getConfig().putSingle(CACHE_POLICY, CachePolicy.EVICT_DAILY.name());
+        propProviderRW.getConfig().putSingle(EVICTION_HOUR, Integer.toString(eviction.get(HOUR_OF_DAY)));
+        propProviderRW.getConfig().putSingle(EVICTION_MINUTE, Integer.toString(eviction.get(MINUTE)));
+        testRealmResource().components().component(propProviderRWId).update(propProviderRW);
+
+        // now
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("thor", realm);
+            System.out.println("User class: " + user.getClass());
+            Assert.assertTrue(user instanceof CachedUserModel); // should still be cached
+        });
+
+        setTimeOffset(2 * 60 * 60); // 2 hours in future
+
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("thor", realm);
+            System.out.println("User class: " + user.getClass());
+            Assert.assertFalse(user instanceof CachedUserModel); // should be evicted
+        });
+
+    }
+
+    @Test
+    public void testWeeklyEviction() {
+        ApiUtil.findUserByUsername(testRealmResource(), "thor");
+
+        // set eviction to 4 days from now
+        Calendar eviction = Calendar.getInstance();
+        eviction.add(Calendar.HOUR, 4 * 24);
+        ComponentRepresentation propProviderRW = testRealmResource().components().component(propProviderRWId).toRepresentation();
+        propProviderRW.getConfig().putSingle(CACHE_POLICY, CachePolicy.EVICT_WEEKLY.name());
+        propProviderRW.getConfig().putSingle(EVICTION_DAY, Integer.toString(eviction.get(DAY_OF_WEEK)));
+        propProviderRW.getConfig().putSingle(EVICTION_HOUR, Integer.toString(eviction.get(HOUR_OF_DAY)));
+        propProviderRW.getConfig().putSingle(EVICTION_MINUTE, Integer.toString(eviction.get(MINUTE)));
+        testRealmResource().components().component(propProviderRWId).update(propProviderRW);
+
+        // now
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("thor", realm);
+            System.out.println("User class: " + user.getClass());
+            Assert.assertTrue(user instanceof CachedUserModel); // should still be cached
+        });
+
+        setTimeOffset(2 * 24 * 60 * 60); // 2 days in future
+
+        // now
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("thor", realm);
+            System.out.println("User class: " + user.getClass());
+            Assert.assertTrue(user instanceof CachedUserModel); // should still be cached
+        });
+
+        setTimeOffset(5 * 24 * 60 * 60); // 5 days in future
+
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("thor", realm);
+            System.out.println("User class: " + user.getClass());
+            Assert.assertFalse(user instanceof CachedUserModel); // should be evicted
+        });
+
+    }
+
+    @Test
+    @Ignore
+    public void testMaxLifespan() {
+        ApiUtil.findUserByUsername(testRealmResource(), "thor");
+
+        // set eviction to 1 hour from now
+        ComponentRepresentation propProviderRW = testRealmResource().components().component(propProviderRWId).toRepresentation();
+        propProviderRW.getConfig().putSingle(CACHE_POLICY, CachePolicy.MAX_LIFESPAN.name());
+        propProviderRW.getConfig().putSingle(MAX_LIFESPAN, Long.toString(1 * 60 * 60 * 1000)); // 1 hour in milliseconds
+        testRealmResource().components().component(propProviderRWId).update(propProviderRW);
+
+        // now
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("thor", realm);
+            System.out.println("User class: " + user.getClass());
+            Assert.assertTrue(user instanceof CachedUserModel); // should still be cached
+        });
+
+        setTimeOffset(1/2 * 60 * 60); // 1/2 hour in future
+        
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("thor", realm);
+            System.out.println("User class: " + user.getClass());
+            Assert.assertTrue(user instanceof CachedUserModel); // should still be cached
+        });
+
+        setTimeOffset(2 * 60 * 60); // 2 hours in future
+
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("thor", realm);
+            System.out.println("User class: " + user.getClass());
+            Assert.assertFalse(user instanceof CachedUserModel); // should be evicted
+        });
+
+    }
+
+    @Test
+    public void testNoCache() {
+        ApiUtil.findUserByUsername(testRealmResource(), "thor");
+
+        // set NO_CACHE policy
+        ComponentRepresentation propProviderRW = testRealmResource().components().component(propProviderRWId).toRepresentation();
+        propProviderRW.getConfig().putSingle(CACHE_POLICY, CachePolicy.NO_CACHE.name());
+        testRealmResource().components().component(propProviderRWId).update(propProviderRW);
+
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("thor", realm);
+            System.out.println("User class: " + user.getClass());
+            Assert.assertFalse(user instanceof CachedUserModel); // should be evicted
+        });
+    }
+
+    @Test
+    public void testLifecycle() {
+
+        testingClient.server().run(session -> {
+            UserMapStorage.allocations.set(0);
+            UserMapStorage.closings.set(0);
+
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().addUser(realm, "memuser");
+            Assert.assertNotNull(user);
+            user = session.users().getUserByUsername("nonexistent", realm);
+            Assert.assertNull(user);
+
+            Assert.assertEquals(1, UserMapStorage.allocations.get());
+            Assert.assertEquals(0, UserMapStorage.closings.get());
+        });
+
+        testingClient.server().run(session -> {
+            Assert.assertEquals(1, UserMapStorage.allocations.get());
+            Assert.assertEquals(1, UserMapStorage.closings.get());
+        });
+
+    }
+
+    @Test
+    public void testEntityRemovalHooks() {
+        testingClient.server().run(session -> {
+            UserMapStorage.realmRemovals.set(0);
+            UserMapStorage.groupRemovals.set(0);
+            UserMapStorage.roleRemovals.set(0);
+        });
+
+        // remove group
+        GroupRepresentation g1 = new GroupRepresentation();
+        g1.setName("group1");
+        GroupRepresentation g2 = new GroupRepresentation();
+        g2.setName("group2");
+        String gid1 = ApiUtil.getCreatedId(testRealmResource().groups().add(g1));
+        String gid2 = ApiUtil.getCreatedId(testRealmResource().groups().add(g2));
+        testRealmResource().groups().group(gid1).remove();
+        testRealmResource().groups().group(gid2).remove();
+        testingClient.server().run(session -> {
+            Assert.assertEquals(2, UserMapStorage.groupRemovals.get());
+            UserMapStorage.realmRemovals.set(0);
+        });
+
+        // remove role
+        RoleRepresentation role1 = new RoleRepresentation();
+        role1.setName("role1");
+        RoleRepresentation role2 = new RoleRepresentation();
+        role2.setName("role2");
+        testRealmResource().roles().create(role1);
+        testRealmResource().roles().create(role2);
+        testRealmResource().roles().get("role1").remove();
+        testRealmResource().roles().get("role2").remove();
+        testingClient.server().run(session -> {
+            Assert.assertEquals(2, UserMapStorage.roleRemovals.get());
+            UserMapStorage.realmRemovals.set(0);
+        });
+
+        // remove realm
+        RealmRepresentation testRealmRepresentation = testRealmResource().toRepresentation();
+        testRealmResource().remove();
+        testingClient.server().run(session -> {
+            Assert.assertEquals(1, UserMapStorage.realmRemovals.get());
+            UserMapStorage.realmRemovals.set(0);
+        });
+
+    }
+
+    @Test
+    @Ignore
+    public void testEntityRemovalHooksCascade() {
+        testingClient.server().run(session -> {
+            UserMapStorage.realmRemovals.set(0);
+            UserMapStorage.groupRemovals.set(0);
+            UserMapStorage.roleRemovals.set(0);
+        });
+
+        GroupRepresentation g1 = new GroupRepresentation();
+        g1.setName("group1");
+        GroupRepresentation g2 = new GroupRepresentation();
+        g2.setName("group2");
+        String gid1 = ApiUtil.getCreatedId(testRealmResource().groups().add(g1));
+        String gid2 = ApiUtil.getCreatedId(testRealmResource().groups().add(g2));
+
+        RoleRepresentation role1 = new RoleRepresentation();
+        role1.setName("role1");
+        RoleRepresentation role2 = new RoleRepresentation();
+        role2.setName("role2");
+        testRealmResource().roles().create(role1);
+        testRealmResource().roles().create(role2);
+
+        // remove realm with groups and roles in it
+        testRealmResource().remove();
+        testingClient.server().run(session -> {
+            Assert.assertEquals(1, UserMapStorage.realmRemovals.get());
+            Assert.assertEquals(2, UserMapStorage.groupRemovals.get()); // check if group removal hooks were called
+            Assert.assertEquals(2, UserMapStorage.roleRemovals.get()); // check if role removal hooks were called
+        });
+
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/storage-test/read-only-user-password.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/storage-test/read-only-user-password.properties
new file mode 100644
index 0000000..c0b76ab
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/storage-test/read-only-user-password.properties
@@ -0,0 +1,4 @@
+tbrady=goat
+rob=pw
+jules=pw
+danny=pw
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/storage-test/user-password.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/storage-test/user-password.properties
new file mode 100644
index 0000000..a6e28c1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/storage-test/user-password.properties
@@ -0,0 +1,4 @@
+thor=hammer
+zeus=pw
+apollo=pw
+perseus=pw
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/pom.xml b/testsuite/integration-arquillian/tests/other/pom.xml
index 5063eb0..27cd8e9 100644
--- a/testsuite/integration-arquillian/tests/other/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/pom.xml
@@ -131,12 +131,6 @@
             </modules>
         </profile>
         <profile>
-            <id>console-ui-no-users-tests</id>
-            <modules>
-                <module>console_no_users</module>
-            </modules>
-        </profile>
-        <profile>
             <id>mod_auth_mellon</id>
             <modules>
                 <module>mod_auth_mellon</module>