diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml index d851c9b522..97beccfe76 100755 --- a/model/jpa/pom.xml +++ b/model/jpa/pom.xml @@ -117,10 +117,6 @@ org.slf4j slf4j-log4j12 - - org.keycloak - keycloak-saml-core - ${jdbc.mvn.groupId} ${jdbc.mvn.artifactId} diff --git a/server-spi/src/main/java/org/keycloak/models/UserModelDefaultMethods.java b/server-spi/src/main/java/org/keycloak/models/UserModelDefaultMethods.java index 179a4030bb..7bfb1cbd33 100644 --- a/server-spi/src/main/java/org/keycloak/models/UserModelDefaultMethods.java +++ b/server-spi/src/main/java/org/keycloak/models/UserModelDefaultMethods.java @@ -56,6 +56,11 @@ public abstract class UserModelDefaultMethods implements UserModel { setSingleAttribute(EMAIL, email); } + @Override + public String toString() { + return getClass().getName() + "@" + getId(); + } + /** * The {@link UserModelDefaultMethods.Streams} class extends the {@link UserModelDefaultMethods} abstract class and * implements the {@link UserModel.Streams} interface, allowing subclasses to focus on the implementation of the diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/BackwardsCompatibilityUserStorageFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/BackwardsCompatibilityUserStorageFactory.java index 7e9c43f1e7..5c38da3bc1 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/BackwardsCompatibilityUserStorageFactory.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/BackwardsCompatibilityUserStorageFactory.java @@ -18,12 +18,12 @@ package org.keycloak.testsuite.federation; -import java.util.Hashtable; import java.util.Map; import org.keycloak.component.ComponentModel; import org.keycloak.models.KeycloakSession; import org.keycloak.storage.UserStorageProviderFactory; +import java.util.concurrent.ConcurrentHashMap; /** * @author Marek Posolda @@ -32,7 +32,7 @@ public class BackwardsCompatibilityUserStorageFactory implements UserStorageProv public static final String PROVIDER_ID = "backwards-compatibility-storage"; - protected Map userPasswords = new Hashtable<>(); + private final Map userPasswords = new ConcurrentHashMap<>(); @Override public BackwardsCompatibilityUserStorage create(KeycloakSession session, ComponentModel model) { diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml new file mode 100644 index 0000000000..d403365417 --- /dev/null +++ b/testsuite/model/pom.xml @@ -0,0 +1,149 @@ + + + 4.0.0 + + org.keycloak + keycloak-testsuite-pom + 12.0.0-SNAPSHOT + ../pom.xml + + + org.keycloak.testsuite + keycloak-model-test + Tests for logical storage layer + Tests for storage layer functionality targetting logical layer, i.e. models + jar + + + org.h2.Driver + keycloak + sa + + jdbc:h2:mem:test;MVCC=TRUE;DB_CLOSE_DELAY=-1 + com.h2database + h2 + ${h2.version} + file:${project.build.directory}/dependency/log4j.properties + + + + + junit + junit + test + + + org.hamcrest + hamcrest-all + test + + + log4j + log4j + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-log4j12 + + + org.keycloak + keycloak-services + + + org.keycloak + keycloak-server-spi-private + + + org.keycloak + keycloak-server-spi + + + ${jdbc.mvn.groupId} + ${jdbc.mvn.artifactId} + ${jdbc.mvn.version} + test + + + + org.keycloak + keycloak-model-jpa + + + org.keycloak + keycloak-model-infinispan + + + org.keycloak + keycloak-model-map + + + org.keycloak.testsuite + integration-arquillian-testsuite-providers + ${project.version} + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + ${keycloak.model.parameters} + ${keycloak.connectionsJpa.driver} + ${keycloak.connectionsJpa.database} + ${keycloak.connectionsJpa.user} + ${keycloak.connectionsJpa.password} + ${keycloak.connectionsJpa.url} + file:${project.build.directory}/test-classes/log4j.properties + + + + + + + + + jpa + + Jpa + + + + + jpa+infinispan + + Infinispan,Jpa + + + + + jpa-federation+infinispan + + Infinispan,JpaFederation,BackwardsCompatibilityUserStorage + + + + + jpa-federation + + JpaFederation,BackwardsCompatibilityUserStorage + + + + + map+infinispan + + Infinispan,Map,ConcurrentHashMapStorage + + + + + \ No newline at end of file diff --git a/testsuite/model/src/test/java/org/keycloak/model/KeycloakModelParameters.java b/testsuite/model/src/test/java/org/keycloak/model/KeycloakModelParameters.java new file mode 100644 index 0000000000..cd6af346f2 --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/KeycloakModelParameters.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 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.model; + +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; +import java.util.Set; + +/** + * + * @author hmlnarik + */ +public class KeycloakModelParameters { + + private final Set> allowedSpis; + private final Set> allowedFactories; + + public KeycloakModelParameters(Set> allowedSpis, Set> allowedFactories) { + this.allowedSpis = allowedSpis; + this.allowedFactories = allowedFactories; + } + + boolean isSpiAllowed(Spi s) { + return allowedSpis.contains(s.getClass()); + } + + boolean isFactoryAllowed(ProviderFactory factory) { + return allowedFactories.stream().anyMatch((c) -> c.isAssignableFrom(factory.getClass())); + } + +} diff --git a/testsuite/model/src/test/java/org/keycloak/model/KeycloakModelTest.java b/testsuite/model/src/test/java/org/keycloak/model/KeycloakModelTest.java new file mode 100644 index 0000000000..2f4533c942 --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/KeycloakModelTest.java @@ -0,0 +1,226 @@ +/* + * Copyright 2020 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.model; + +import org.keycloak.Config.Scope; +import org.keycloak.authorization.AuthorizationSpi; +import org.keycloak.authorization.DefaultAuthorizationProviderFactory; +import org.keycloak.authorization.store.StoreFactorySpi; +import org.keycloak.cluster.ClusterSpi; +import org.keycloak.component.ComponentModel; +import org.keycloak.events.EventStoreSpi; +import org.keycloak.executors.DefaultExecutorsProviderFactory; +import org.keycloak.executors.ExecutorsSpi; +import org.keycloak.models.AbstractKeycloakTransaction; +import org.keycloak.models.ClientSpi; +import org.keycloak.models.GroupSpi; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RealmSpi; +import org.keycloak.models.RoleSpi; +import org.keycloak.models.UserSpi; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.ProviderManager; +import org.keycloak.provider.Spi; +import org.keycloak.services.DefaultKeycloakSession; +import org.keycloak.services.DefaultKeycloakSessionFactory; +import org.keycloak.storage.UserStorageProvider; +import org.keycloak.storage.UserStorageProviderModel; +import com.google.common.collect.ImmutableSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.hamcrest.Matchers; +import org.jboss.logging.Logger; +import org.junit.After; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertThat; + +/** + * Base of testcases that operate on session level. The tests derived from this class + * will have access to a shared {@link KeycloakSessionFactory} in the {@link #FACTORY} + * field that can be used to obtain a session and e.g. start / stop transaction. + *

+ * This class expects {@code keycloak.model.parameters} system property to contain + * comma-separated class names that implement {@link KeycloakModelParameters} interface + * to provide list of factories and SPIs that are visible to the {@link KeycloakSessionFactory} + * that is offered to the tests. + *

+ * If no parameters are set via this property, the tests derived from this class are skipped. + * @author hmlnarik + */ +public abstract class KeycloakModelTest { + + private static final Logger LOG = Logger.getLogger(KeycloakModelParameters.class); + protected final Logger log = Logger.getLogger(getClass()); + + @ClassRule + public static final TestRule GUARANTEE_REQUIRED_FACTORY = new TestRule() { + @Override + public Statement apply(Statement base, Description description) { + Class testClass = description.getTestClass(); + while (testClass != Object.class) { + for (RequireProvider ann : testClass.getAnnotationsByType(RequireProvider.class)) { + Assume.assumeThat("Provider must exist: " + ann.value(), FACTORY.getProviderFactory(ann.value()), Matchers.notNullValue()); + } + testClass = testClass.getSuperclass(); + } + return base; + } + }; + + private static final Set> ALLOWED_SPIS = ImmutableSet.>builder() + .add(AuthorizationSpi.class) + .add(ClientSpi.class) + .add(ClusterSpi.class) + .add(EventStoreSpi.class) + .add(ExecutorsSpi.class) + .add(GroupSpi.class) + .add(RealmSpi.class) + .add(RoleSpi.class) + .add(StoreFactorySpi.class) + .add(UserSpi.class) + .build(); + + private static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() + .add(DefaultAuthorizationProviderFactory.class) + .add(DefaultExecutorsProviderFactory.class) + .build(); + + protected static final List MODEL_PARAMETERS; + protected static final DefaultKeycloakSessionFactory FACTORY; + + static { + KeycloakModelParameters basicParameters = new KeycloakModelParameters(ALLOWED_SPIS, ALLOWED_FACTORIES); + MODEL_PARAMETERS = Stream.concat( + Stream.of(basicParameters), + Stream.of(System.getProperty("keycloak.model.parameters", "").split("\\s*,\\s*")) + .filter(s -> s != null && ! s.trim().isEmpty()) + .map(cn -> { try { return Class.forName(cn.indexOf('.') >= 0 ? cn : ("org.keycloak.model.parameters." + cn)); } catch (Exception e) { LOG.error("Cannot find " + cn); return null; }}) + .filter(Objects::nonNull) + .map(c -> { try { return c.newInstance(); } catch (Exception e) { LOG.error("Cannot instantiate " + c); return null; }} ) + .filter(KeycloakModelParameters.class::isInstance) + .map(KeycloakModelParameters.class::cast) + ) + .collect(Collectors.toList()); + + FACTORY = new DefaultKeycloakSessionFactory() { + @Override + protected boolean isEnabled(ProviderFactory factory, Scope scope) { + return super.isEnabled(factory, scope) && isFactoryAllowed(factory); + } + + @Override + protected Map, Map> loadFactories(ProviderManager pm) { + spis.removeIf(s -> ! isSpiAllowed(s)); + return super.loadFactories(pm); + } + + private boolean isSpiAllowed(Spi s) { + return MODEL_PARAMETERS.stream().anyMatch(p -> p.isSpiAllowed(s)); + } + + private boolean isFactoryAllowed(ProviderFactory factory) { + return MODEL_PARAMETERS.stream().anyMatch(p -> p.isFactoryAllowed(factory)); + } + }; + FACTORY.init(); + } + + @BeforeClass + public static void checkValidParameters() { + Assume.assumeTrue("keycloak.model.parameters property must be set", MODEL_PARAMETERS.size() > 1); // Additional parameters have to be set + } + + protected void createEnvironment(KeycloakSession s) { + } + + protected void cleanEnvironment(KeycloakSession s) { + } + + @Before + public void createEnvironment() { + KeycloakModelUtils.runJobInTransaction(FACTORY, this::createEnvironment); + } + + @After + public void cleanEnvironment() { + KeycloakModelUtils.runJobInTransaction(FACTORY, this::cleanEnvironment); + } + + protected String registerUserFederationIfAvailable(RealmModel realm) { + final List userFedProviders = FACTORY.getProviderFactories(UserStorageProvider.class); + + if (! userFedProviders.isEmpty() && realm != null) { + assertThat("Cannot handle more than 1 user federation provider", userFedProviders, hasSize(1)); + UserStorageProviderModel federatedStorage = new UserStorageProviderModel(); + federatedStorage.setName(userFedProviders.get(0).getId()); + federatedStorage.setProviderId(userFedProviders.get(0).getId()); + federatedStorage.setProviderType(UserStorageProvider.class.getName()); + federatedStorage.setParentId(realm.getId()); + ComponentModel res = realm.addComponentModel(federatedStorage); + log.infof("Added %s user federation provider: %s", federatedStorage.getName(), res.getId()); + return res.getId(); + } + return null; + } + + protected void inRolledBackTransaction(T parameter, BiConsumer what) { + KeycloakSession session = new DefaultKeycloakSession(FACTORY); + session.getTransactionManager().begin(); + + what.accept(session, parameter); + + session.getTransactionManager().rollback(); + } + + protected void inComittedTransaction(T parameter, BiConsumer what) { + inComittedTransaction(parameter, what, (a,b) -> {}, (a,b) -> {}); + } + + protected void inComittedTransaction(T parameter, BiConsumer what, BiConsumer onCommit, BiConsumer onRollback) { + KeycloakModelUtils.runJobInTransaction(FACTORY, session -> { + session.getTransactionManager().enlistAfterCompletion(new AbstractKeycloakTransaction() { + @Override + protected void commitImpl() { + if (onCommit != null) { onCommit.accept(session, parameter); } + } + + @Override + protected void rollbackImpl() { + if (onRollback != null) { onRollback.accept(session, parameter); } + } + }); + what.accept(session, parameter); + }); + } + +} diff --git a/testsuite/model/src/test/java/org/keycloak/model/RequireProvider.java b/testsuite/model/src/test/java/org/keycloak/model/RequireProvider.java new file mode 100644 index 0000000000..bb7a70a51c --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/RequireProvider.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020 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.model; + +import org.keycloak.provider.Provider; +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Identifies a requirement for a given provider to be present in the session factory. + * If the provider is not available, the test is skipped. + * + * @author hmlnarik + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Repeatable(RequireProviders.class) +public @interface RequireProvider { + Class value() default Provider.class; + +} diff --git a/testsuite/model/src/test/java/org/keycloak/model/RequireProviders.java b/testsuite/model/src/test/java/org/keycloak/model/RequireProviders.java new file mode 100644 index 0000000000..0786125567 --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/RequireProviders.java @@ -0,0 +1,32 @@ +/* + * Copyright 2020 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.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author hmlnarik + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface RequireProviders { + RequireProvider[] value(); +} diff --git a/testsuite/model/src/test/java/org/keycloak/model/UserModelTest.java b/testsuite/model/src/test/java/org/keycloak/model/UserModelTest.java new file mode 100644 index 0000000000..1210d6c3b1 --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/UserModelTest.java @@ -0,0 +1,235 @@ +/* + * Copyright 2020 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.model; + +import org.keycloak.component.ComponentModel; +import org.keycloak.models.GroupModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RealmProvider; +import org.keycloak.models.UserModel; +import org.keycloak.models.UserProvider; +import org.keycloak.storage.UserStorageProvider; +import org.keycloak.storage.UserStorageProviderFactory; +import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.user.UserLookupProvider; +import org.keycloak.storage.user.UserRegistrationProvider; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.hamcrest.Matchers; +import org.junit.Test; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeThat; + +/** + * + * @author hmlnarik + */ +@RequireProvider(UserProvider.class) +@RequireProvider(RealmProvider.class) +public class UserModelTest extends KeycloakModelTest { + + protected static final int NUM_GROUPS = 100; + + private String realmId; + private final List groupIds = new ArrayList<>(NUM_GROUPS); + private String userFederationId; + + @Override + public void createEnvironment(KeycloakSession s) { + RealmModel realm = s.realms().createRealm("realm"); + this.realmId = realm.getId(); + + this.userFederationId = registerUserFederationIfAvailable(realm); + + IntStream.range(0, NUM_GROUPS).forEach(i -> { + groupIds.add(s.groups().createGroup(realm, "group-" + i).getId()); + }); + } + + @Override + public void cleanEnvironment(KeycloakSession s) { + s.realms().removeRealm(realmId); + } + + private void addRemoveUser(KeycloakSession session, int i) { + RealmModel realm = session.realms().getRealmByName("realm"); + + UserModel user = session.users().addUser(realm, "user-" + i); + + IntStream.range(0, NUM_GROUPS / 20).forEach(gIndex -> { + user.joinGroup(session.groups().getGroupById(realm, groupIds.get((i + gIndex) % NUM_GROUPS))); + }); + + final UserModel obtainedUser = session.users().getUserById(user.getId(), realm); + + assertThat(obtainedUser, Matchers.notNullValue()); + assertThat(obtainedUser.getUsername(), is("user-" + i)); + Set userGroupIds = obtainedUser.getGroupsStream().map(GroupModel::getName).collect(Collectors.toSet()); + assertThat(userGroupIds, hasSize(NUM_GROUPS / 20)); + assertThat(userGroupIds, hasItem("group-" + i)); + assertThat(userGroupIds, hasItem("group-" + (i - 1 + (NUM_GROUPS / 20)) % NUM_GROUPS)); + + assertTrue(session.users().removeUser(realm, user)); + assertFalse(session.users().removeUser(realm, user)); + } + + @Test + public void testAddRemoveUser() { + inRolledBackTransaction(1, this::addRemoveUser); + } + + @Test + public void testAddRemoveUserConcurrent() { + IntStream.range(0,100).parallel().forEach(i -> inComittedTransaction(i, this::addRemoveUser)); + } + + @Test + public void testAddRemoveUsersInTheSameGroupConcurrent() { + final ConcurrentSkipListSet userIds = new ConcurrentSkipListSet<>(); + String groupId = groupIds.get(0); + + // Create users and let them join first group + IntStream.range(0, 100).parallel().forEach(index -> inComittedTransaction(index, (session, i) -> { + final RealmModel realm = session.realms().getRealm(realmId); + final UserModel user = session.users().addUser(realm, "user-" + i); + user.joinGroup(session.groups().getGroupById(realm, groupId)); + userIds.add(user.getId()); + })); + + inComittedTransaction(1, (session, i) -> { + final RealmModel realm = session.realms().getRealm(realmId); + final GroupModel group = session.groups().getGroupById(realm, groupId); + assertThat(session.users().getGroupMembersStream(realm, group).count(), is(100L)); + }); + + // Some of the transactions may fail due to conflicts as there are many parallel request, so repeat until all users are removed + Set remainingUserIds = new HashSet<>(); + do { + userIds.stream().parallel().forEach(index -> inComittedTransaction(index, (session, userId) -> { + final RealmModel realm = session.realms().getRealm(realmId); + final UserModel user = session.users().getUserById(userId, realm); + log.debugf("Remove user %s: %s", userId, session.users().removeUser(realm, user)); + }, null, (session, userId) -> remainingUserIds.add(userId) )); + + userIds.clear(); + userIds.addAll(remainingUserIds); + remainingUserIds.clear(); + } while (! userIds.isEmpty()); + + inComittedTransaction(1, (session, i) -> { + final RealmModel realm = session.realms().getRealm(realmId); + final GroupModel group = session.groups().getGroupById(realm, groupId); + assertThat(session.users().getGroupMembersStream(realm, group).collect(Collectors.toList()), Matchers.empty()); + }); + } + + @Test + public void testAddDirtyRemoveFederationUsersInTheSameGroupConcurrent() { + assumeThat("Test for federated providers only", userFederationId, Matchers.notNullValue()); + + final ConcurrentSkipListSet userIds = new ConcurrentSkipListSet<>(); + String groupId = groupIds.get(0); + + // Create users and let them join first group + IntStream.range(0, 100).parallel().forEach(index -> inComittedTransaction(index, (session, i) -> { + final RealmModel realm = session.realms().getRealm(realmId); + final UserModel user = session.users().addUser(realm, "user-" + i); + user.joinGroup(session.groups().getGroupById(realm, groupId)); + userIds.add(user.getId()); + })); + + // Remove users _from the federation_, simulates eg. user being removed from LDAP without Keycloak knowing + inComittedTransaction(1, (session, i) -> { + final RealmModel realm = session.realms().getRealm(realmId); + UserStorageProvider instance = (UserStorageProvider)session.getAttribute(userFederationId); + + if (instance == null) { + ComponentModel model = realm.getComponent(userFederationId); + UserStorageProviderModel storageModel = new UserStorageProviderModel(model); + UserStorageProviderFactory factory = (UserStorageProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, model.getProviderId()); + instance = factory.create(session, model); + if (instance == null) { + throw new RuntimeException("UserStorageProvideFactory (of type " + factory.getClass().getName() + ") produced a null instance"); + } + session.enlistForClose(instance); + session.setAttribute(userFederationId, instance); + } + + final UserStorageProvider lambdaInstance = instance; + log.debugf("Removing selected users from backend"); + IntStream.range(FIRST_DELETED_USER_INDEX, LAST_DELETED_USER_INDEX).forEach(j -> { + final UserModel user = ((UserLookupProvider) lambdaInstance).getUserByUsername("user-" + j, realm); + ((UserRegistrationProvider) lambdaInstance).removeUser(realm, user); + }); + }); + + inComittedTransaction(1, (session, i) -> { + final RealmModel realm = session.realms().getRealm(realmId); + final GroupModel group = session.groups().getGroupById(realm, groupId); + assertThat(session.users().getGroupMembersStream(realm, group).count(), is(100L - DELETED_USER_COUNT)); + }); + + // Now delete the users, and count those that were not found to be deleted. This should be equal to the number + // of users removed directly in the user federation. + // Some of the transactions may fail due to conflicts as there are many parallel request, so repeat until all users are removed + AtomicInteger notFoundUsers = new AtomicInteger(); + Set remainingUserIds = new HashSet<>(); + do { + userIds.stream().parallel().forEach(index -> inComittedTransaction(index, (session, userId) -> { + final RealmModel realm = session.realms().getRealm(realmId); + final UserModel user = session.users().getUserById(userId, realm); + if (user != null) { + log.debugf("Deleting user: %s", userId); + session.users().removeUser(realm, user); + } else { + log.debugf("Failed deleting user: %s", userId); + notFoundUsers.incrementAndGet(); + } + }, null, (session, userId) -> { + log.debugf("Could not delete user %s", userId); + remainingUserIds.add(userId); + })); + + userIds.clear(); + userIds.addAll(remainingUserIds); + remainingUserIds.clear(); + } while (! userIds.isEmpty()); + + assertThat(notFoundUsers.get(), is(DELETED_USER_COUNT)); + + inComittedTransaction(1, (session, i) -> { + final RealmModel realm = session.realms().getRealm(realmId); + final GroupModel group = session.groups().getGroupById(realm, groupId); + assertThat(session.users().getGroupMembersStream(realm, group).collect(Collectors.toList()), Matchers.empty()); + }); + } + private static final int FIRST_DELETED_USER_INDEX = 10; + private static final int LAST_DELETED_USER_INDEX = 90; + private static final int DELETED_USER_COUNT = LAST_DELETED_USER_INDEX - FIRST_DELETED_USER_INDEX; +} diff --git a/model/jpa/src/test/java/org/keycloak/events/jpa/JpaAdminEventQueryTest.java b/testsuite/model/src/test/java/org/keycloak/model/events/AdminEventQueryTest.java similarity index 51% rename from model/jpa/src/test/java/org/keycloak/events/jpa/JpaAdminEventQueryTest.java rename to testsuite/model/src/test/java/org/keycloak/model/events/AdminEventQueryTest.java index 4f06ff2ea4..98985f4122 100644 --- a/model/jpa/src/test/java/org/keycloak/events/jpa/JpaAdminEventQueryTest.java +++ b/testsuite/model/src/test/java/org/keycloak/model/events/AdminEventQueryTest.java @@ -14,36 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.events.jpa; +package org.keycloak.model.events; -import org.keycloak.Config.Scope; import org.keycloak.common.ClientConnection; -import org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory; -import org.keycloak.connections.jpa.JpaConnectionSpi; -import org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory; -import org.keycloak.connections.jpa.updater.JpaUpdaterSpi; -import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProviderFactory; -import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionSpi; -import org.keycloak.connections.jpa.updater.liquibase.lock.LiquibaseDBLockProviderFactory; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventStoreProvider; -import org.keycloak.events.EventStoreProviderFactory; -import org.keycloak.events.EventStoreSpi; import org.keycloak.events.EventType; +import org.keycloak.model.KeycloakModelTest; +import org.keycloak.model.RequireProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; -import org.keycloak.models.RealmSpi; -import org.keycloak.models.dblock.DBLockSpi; -import org.keycloak.models.jpa.JpaRealmProviderFactory; -import org.keycloak.provider.Provider; -import org.keycloak.provider.ProviderFactory; -import org.keycloak.provider.ProviderManager; -import org.keycloak.provider.Spi; -import org.keycloak.services.DefaultKeycloakSession; -import org.keycloak.services.DefaultKeycloakSessionFactory; -import com.google.common.collect.ImmutableSet; -import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; import org.junit.After; import org.junit.Before; @@ -55,43 +35,17 @@ import static org.junit.Assert.assertThat; * * @author hmlnarik */ -public class JpaAdminEventQueryTest { +@RequireProvider(EventStoreProvider.class) +public class AdminEventQueryTest extends KeycloakModelTest { - private static final Set> ALLOWED_SPIS = ImmutableSet.>builder() - .add(DBLockSpi.class) - .add(EventStoreSpi.class) - .add(JpaConnectionSpi.class) - .add(JpaUpdaterSpi.class) - .add(LiquibaseConnectionSpi.class) - .add(RealmSpi.class) - .build(); - - private static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() - .add(DefaultJpaConnectionProviderFactory.class) - .add(EventStoreProviderFactory.class) - .add(JpaUpdaterProviderFactory.class) - .add(JpaRealmProviderFactory.class) - .add(LiquibaseConnectionProviderFactory.class) - .add(LiquibaseDBLockProviderFactory.class) - .build(); - - private static final DefaultKeycloakSessionFactory factory = new DefaultKeycloakSessionFactory() { - @Override - protected boolean isEnabled(ProviderFactory factory, Scope scope) { - return super.isEnabled(factory, scope) && ALLOWED_FACTORIES.stream().filter(c -> c.isAssignableFrom(factory.getClass())).findAny().isPresent(); - } - - @Override - protected Map, Map> loadFactories(ProviderManager pm) { - spis.removeIf(s -> ! ALLOWED_SPIS.contains(s.getClass())); - return super.loadFactories(pm); - } - }; - static { factory.init(); } - - private final KeycloakSession session = new DefaultKeycloakSession(factory); + private final KeycloakSession session = FACTORY.create(); private final EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class); + @Test + public void testClear() { + eventStore.clear(); + } + @Before public void startTransaction() { session.getTransactionManager().begin(); @@ -102,11 +56,6 @@ public class JpaAdminEventQueryTest { session.getTransactionManager().rollback(); } - @Test - public void testClear() { - eventStore.clear(); - } - @Test public void testQuery() { RealmModel realm = session.realms().createRealm("realm"); diff --git a/testsuite/model/src/test/java/org/keycloak/model/parameters/BackwardsCompatibilityUserStorage.java b/testsuite/model/src/test/java/org/keycloak/model/parameters/BackwardsCompatibilityUserStorage.java new file mode 100644 index 0000000000..a644e25f4f --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/parameters/BackwardsCompatibilityUserStorage.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 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.model.parameters; + +import org.keycloak.model.KeycloakModelParameters; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; +import org.keycloak.storage.UserStorageProviderSpi; +import org.keycloak.storage.federated.UserFederatedStorageProviderSpi; +import org.keycloak.storage.jpa.JpaUserFederatedStorageProviderFactory; +import org.keycloak.testsuite.federation.BackwardsCompatibilityUserStorageFactory; +import com.google.common.collect.ImmutableSet; +import java.util.Set; + +/** + * + * @author hmlnarik + */ +public class BackwardsCompatibilityUserStorage extends KeycloakModelParameters { + + static final Set> ALLOWED_SPIS = ImmutableSet.>builder() + .build(); + + static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() + .add(BackwardsCompatibilityUserStorageFactory.class) + .build(); + + public BackwardsCompatibilityUserStorage() { + super(ALLOWED_SPIS, ALLOWED_FACTORIES); + } +} diff --git a/testsuite/model/src/test/java/org/keycloak/model/parameters/ConcurrentHashMapStorage.java b/testsuite/model/src/test/java/org/keycloak/model/parameters/ConcurrentHashMapStorage.java new file mode 100644 index 0000000000..537e7a9395 --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/parameters/ConcurrentHashMapStorage.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 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.model.parameters; + +import org.keycloak.model.KeycloakModelParameters; +import org.keycloak.models.map.client.MapClientProviderFactory; +import org.keycloak.models.map.group.MapGroupProviderFactory; +import org.keycloak.models.map.role.MapRoleProviderFactory; +import org.keycloak.models.map.storage.ConcurrentHashMapStorageProvider; +import org.keycloak.models.map.storage.MapStorageProvider; +import org.keycloak.models.map.storage.MapStorageSpi; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; +import com.google.common.collect.ImmutableSet; +import java.util.Set; + +/** + * + * @author hmlnarik + */ +public class ConcurrentHashMapStorage extends KeycloakModelParameters { + + static final Set> ALLOWED_SPIS = ImmutableSet.>builder() + .build(); + + static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() + .add(ConcurrentHashMapStorageProvider.class) + .build(); + + static { + System.setProperty("keycloak.mapStorage.concurrenthashmap.dir", System.getProperty("keycloak.mapStorage.concurrenthashmap.dir", "${project.build.directory:target}")); + } + + public ConcurrentHashMapStorage() { + super(ALLOWED_SPIS, ALLOWED_FACTORIES); + } + +} diff --git a/testsuite/model/src/test/java/org/keycloak/model/parameters/Infinispan.java b/testsuite/model/src/test/java/org/keycloak/model/parameters/Infinispan.java new file mode 100644 index 0000000000..273320fb71 --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/parameters/Infinispan.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 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.model.parameters; + +import org.keycloak.cluster.infinispan.InfinispanClusterProviderFactory; +import org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory; +import org.keycloak.connections.infinispan.InfinispanConnectionSpi; +import org.keycloak.model.KeycloakModelParameters; +import org.keycloak.models.cache.CacheRealmProviderSpi; +import org.keycloak.models.cache.CacheUserProviderSpi; +import org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderFactory; +import org.keycloak.models.cache.infinispan.InfinispanUserCacheProviderFactory; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; +import com.google.common.collect.ImmutableSet; +import java.util.Set; + +/** + * + * @author hmlnarik + */ +public class Infinispan extends KeycloakModelParameters { + + static final Set> ALLOWED_SPIS = ImmutableSet.>builder() + .add(CacheRealmProviderSpi.class) + .add(CacheUserProviderSpi.class) + .add(InfinispanConnectionSpi.class) + + .build(); + + static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() + .add(InfinispanCacheRealmProviderFactory.class) + .add(InfinispanClusterProviderFactory.class) + .add(InfinispanConnectionProviderFactory.class) + .add(InfinispanUserCacheProviderFactory.class) + .build(); + + static { + System.setProperty("keycloak.connectionsInfinispan.default.embedded", System.getProperty("keycloak.connectionsInfinispan.default.embedded", "true")); + } + + public Infinispan() { + super(ALLOWED_SPIS, ALLOWED_FACTORIES); + } +} diff --git a/testsuite/model/src/test/java/org/keycloak/model/parameters/Jpa.java b/testsuite/model/src/test/java/org/keycloak/model/parameters/Jpa.java new file mode 100644 index 0000000000..b0d6c90bcc --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/parameters/Jpa.java @@ -0,0 +1,74 @@ +/* + * Copyright 2020 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.model.parameters; + +import org.keycloak.authorization.jpa.store.JPAAuthorizationStoreFactory; +import org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory; +import org.keycloak.connections.jpa.JpaConnectionSpi; +import org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory; +import org.keycloak.connections.jpa.updater.JpaUpdaterSpi; +import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProviderFactory; +import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionSpi; +import org.keycloak.connections.jpa.updater.liquibase.lock.LiquibaseDBLockProviderFactory; +import org.keycloak.events.jpa.JpaEventStoreProviderFactory; +import org.keycloak.model.KeycloakModelParameters; +import org.keycloak.models.dblock.DBLockSpi; +import org.keycloak.models.jpa.JpaClientProviderFactory; +import org.keycloak.models.jpa.JpaGroupProviderFactory; +import org.keycloak.models.jpa.JpaRealmProviderFactory; +import org.keycloak.models.jpa.JpaRoleProviderFactory; +import org.keycloak.models.jpa.JpaUserProviderFactory; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; +import com.google.common.collect.ImmutableSet; +import java.util.Set; + +/** + * + * @author hmlnarik + */ +public class Jpa extends KeycloakModelParameters { + + static final Set> ALLOWED_SPIS = ImmutableSet.>builder() + // jpa-specific + .add(DBLockSpi.class) + .add(JpaConnectionSpi.class) + .add(JpaUpdaterSpi.class) + .add(LiquibaseConnectionSpi.class) + + .build(); + + static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() + // jpa-specific + .add(DefaultJpaConnectionProviderFactory.class) + .add(JPAAuthorizationStoreFactory.class) + .add(JpaClientProviderFactory.class) + .add(JpaEventStoreProviderFactory.class) + .add(JpaGroupProviderFactory.class) + .add(JpaRealmProviderFactory.class) + .add(JpaRoleProviderFactory.class) + .add(JpaUpdaterProviderFactory.class) + .add(JpaUserProviderFactory.class) + .add(LiquibaseConnectionProviderFactory.class) + .add(LiquibaseDBLockProviderFactory.class) + .build(); + + public Jpa() { + super(ALLOWED_SPIS, ALLOWED_FACTORIES); + } + +} diff --git a/testsuite/model/src/test/java/org/keycloak/model/parameters/JpaFederation.java b/testsuite/model/src/test/java/org/keycloak/model/parameters/JpaFederation.java new file mode 100644 index 0000000000..8e7a422eaf --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/parameters/JpaFederation.java @@ -0,0 +1,50 @@ +/* + * Copyright 2020 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.model.parameters; + +import org.keycloak.model.KeycloakModelParameters; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; +import org.keycloak.storage.UserStorageProviderSpi; +import org.keycloak.storage.federated.UserFederatedStorageProviderSpi; +import org.keycloak.storage.jpa.JpaUserFederatedStorageProviderFactory; +import org.keycloak.testsuite.federation.BackwardsCompatibilityUserStorageFactory; +import com.google.common.collect.ImmutableSet; +import java.util.Set; + +/** + * + * @author hmlnarik + */ +public class JpaFederation extends KeycloakModelParameters { + + static final Set> ALLOWED_SPIS = ImmutableSet.>builder() + .addAll(Jpa.ALLOWED_SPIS) + .add(UserStorageProviderSpi.class) + .add(UserFederatedStorageProviderSpi.class) + + .build(); + + static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() + .addAll(Jpa.ALLOWED_FACTORIES) + .add(JpaUserFederatedStorageProviderFactory.class) + .build(); + + public JpaFederation() { + super(ALLOWED_SPIS, ALLOWED_FACTORIES); + } +} diff --git a/testsuite/model/src/test/java/org/keycloak/model/parameters/Map.java b/testsuite/model/src/test/java/org/keycloak/model/parameters/Map.java new file mode 100644 index 0000000000..1fe04a8643 --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/model/parameters/Map.java @@ -0,0 +1,51 @@ +/* + * Copyright 2020 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.model.parameters; + +import org.keycloak.model.KeycloakModelParameters; +import org.keycloak.models.map.client.MapClientProviderFactory; +import org.keycloak.models.map.group.MapGroupProviderFactory; +import org.keycloak.models.map.role.MapRoleProviderFactory; +import org.keycloak.models.map.storage.MapStorageProvider; +import org.keycloak.models.map.storage.MapStorageSpi; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; +import com.google.common.collect.ImmutableSet; +import java.util.Set; + +/** + * + * @author hmlnarik + */ +public class Map extends KeycloakModelParameters { + + static final Set> ALLOWED_SPIS = ImmutableSet.>builder() + .add(MapStorageSpi.class) + + .build(); + + static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() + .add(MapClientProviderFactory.class) + .add(MapGroupProviderFactory.class) + .add(MapRoleProviderFactory.class) + .add(MapStorageProvider.class) + .build(); + + public Map() { + super(ALLOWED_SPIS, ALLOWED_FACTORIES); + } +} diff --git a/model/jpa/src/test/resources/log4j.properties b/testsuite/model/src/test/resources/log4j.properties similarity index 82% rename from model/jpa/src/test/resources/log4j.properties rename to testsuite/model/src/test/resources/log4j.properties index 2d89ff6a06..1d0c1c002a 100644 --- a/model/jpa/src/test/resources/log4j.properties +++ b/testsuite/model/src/test/resources/log4j.properties @@ -19,18 +19,16 @@ log4j.rootLogger=info, keycloak log4j.appender.keycloak=org.apache.log4j.ConsoleAppender log4j.appender.keycloak.layout=org.apache.log4j.EnhancedPatternLayout -keycloak.testsuite.logging.pattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n +keycloak.testsuite.logging.pattern=%d{HH:mm:ss,SSS} %-5p %t [%c] %m%n log4j.appender.keycloak.layout.ConversionPattern=${keycloak.testsuite.logging.pattern} # Logging with "info" when running test from IDE, but disabled when running test with "mvn" . Both cases can be overriden by use system property "keycloak.logging.level" (eg. -Dkeycloak.logging.level=debug ) -log4j.logger.org.keycloak=${keycloak.logging.level:debug} - -keycloak.testsuite.logging.level=debug -log4j.logger.org.keycloak.testsuite=${keycloak.testsuite.logging.level} +log4j.logger.org.keycloak.models=debug +# log4j.logger.org.hibernate=debug # Enable to view loaded SPI and Providers -log4j.logger.org.keycloak.services.DefaultKeycloakSessionFactory=debug -log4j.logger.org.keycloak.provider.ProviderManager=debug +# log4j.logger.org.keycloak.services.DefaultKeycloakSessionFactory=debug +# log4j.logger.org.keycloak.provider.ProviderManager=debug # log4j.logger.org.keycloak.provider.FileSystemProviderLoaderFactory=debug # Liquibase updates logged with "info" by default. Logging level can be changed by system property "keycloak.liquibase.logging.level" diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 5e4d8f9049..76eae031dd 100755 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -45,6 +45,7 @@ db-allocator-plugin integration-arquillian + model utils