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 0000000000..b2f80bbeea
--- /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 Bill Burke
+ * @version $Revision: 1 $
+ */
+public class UserMapStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, CredentialInputUpdater, CredentialInputValidator {
+
+ private static final Logger log = Logger.getLogger(UserMapStorage.class);
+
+ protected Map 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 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 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 0000000000..885c3164ce
--- /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 Bill Burke
+ * @version $Revision: 1 $
+ */
+public class UserMapStorageFactory implements UserStorageProviderFactory {
+
+
+ public static final String PROVIDER_ID = "user-password-map-arq";
+
+ protected Map 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 0000000000..3716afffff
--- /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 Bill Burke
+ * @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 getUsers(RealmModel realm) {
+ List users = new LinkedList<>();
+ for (Object username : userPasswords.keySet()) {
+ users.add(createUser(realm, (String)username));
+ }
+ return users;
+ }
+
+ @Override
+ public List searchForUser(Map attributes, RealmModel realm) {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
+ public List getUsers(RealmModel realm, int firstResult, int maxResults) {
+ if (maxResults == 0) return Collections.EMPTY_LIST;
+ List 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 searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
+ if (maxResults == 0) return Collections.EMPTY_LIST;
+ List 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 searchForUser(Map 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 getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
+ public List getGroupMembers(RealmModel realm, GroupModel group) {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
+ public List searchForUser(String search, RealmModel realm) {
+ return getUsers(realm, 0, Integer.MAX_VALUE - 1);
+ }
+
+ @Override
+ public List 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 0000000000..7b3f375359
--- /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 Bill Burke
+ * @version $Revision: 1 $
+ */
+public class UserPropertyFileStorageFactory implements UserStorageProviderFactory, 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 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 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 a9ae823e92..7b57770ca3 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,2 +1,4 @@
org.keycloak.testsuite.federation.DummyUserFederationProviderFactory
-org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProviderFactory
\ No newline at end of file
+org.keycloak.testsuite.federation.UserMapStorageFactory
+org.keycloak.testsuite.federation.UserPropertyFileStorageFactory
+org.keycloak.testsuite.federation.PassThroughFederatedUserStorageProviderFactory
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 bd632c35e5..bcb726f3b6 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 classNodes = archive.getContent((ArchivePath path) -> {
@@ -170,25 +165,26 @@ class UndertowDeployerHelper {
for (Map.Entry entry : classNodes.entrySet()) {
Node n = entry.getValue();
- ClassAsset classAsset = (ClassAsset) n.getAsset();
- Class> clazz = classAsset.getSource();
+ 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);
+ 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);
+ 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/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index e079fdcc53..e74016d3cd 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -1,20 +1,20 @@
+~ 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.
+-->
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 570c9e8815..7fbab6a8b7 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/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java
index 97edc00361..bf366cffe5 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 ur = realm.users().search(username, null, null);
+ List 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 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 eed784ba9f..c247009141 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 03df30e9f2..9cb3c32ffe 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 6125ab3eb5..cdd8856fe4 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/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 0000000000..cc57b8e1bf
--- /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 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 0000000000..a5f00a7ae3
--- /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 queried = new HashSet<>();
+ int first = 0;
+ while (queried.size() < 8) {
+ List 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 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 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 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 0000000000..c0b76abe79
--- /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 0000000000..a6e28c1405
--- /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