Map Store Removal: Delete map profiles and scopes from model tests

Closes #24093
This commit is contained in:
Martin Kanis 2023-10-24 15:13:05 +02:00 committed by Alexander Schwartz
parent 8867dd6370
commit e05effe62d
28 changed files with 7 additions and 2409 deletions

View file

@ -49,76 +49,4 @@ mvn test -Pjpa -Dtest=ClientModelTest \
The results are available in the `target/profile.html` file.
Usage of Testcontainers
-----------------------
Some profiles within model tests require running 3rd party software, for
example, database or Infinispan. For running these we are using
[Testcontainers](https://www.testcontainers.org/). This may require some
additional configuration of your container engine.
#### Podman settings
For more details see the following [Podman guide from Quarkus webpage](https://quarkus.io/guides/podman).
Specifically, these steps are required:
```shell
# Enable the podman socket with Docker REST API (only needs to be done once)
systemctl --user enable podman.socket --now
# Set the required environment variables (need to be run everytime or added to profile)
export DOCKER_HOST=unix:///run/user/${UID}/podman/podman.sock
```
Testcontainers are using [ryuk](https://hub.docker.com/r/testcontainers/ryuk)
to cleanup containers after tests. To make this work with Podman add the
following line to `~/.testcontainers.properties`
```shell
ryuk.container.privileged=true
```
Alternatively, disable usage of ryuk (using this may result in stale containers
still running after tests finish. This is not recommended especially if you are
executing tests from Intellij IDE as it [may not stop](https://youtrack.jetbrains.com/issue/IDEA-190385)
the containers created during test run).
```shell
export TESTCONTAINERS_RYUK_DISABLED=true #not recommended - see above!
```
#### Docker settings
To use Testcontainers with Docker it is necessary to
[make Docker available for non-root users](https://docs.docker.com/engine/install/linux-postinstall/).
Running HotRod tests with external Infinispan
---------------------------------------------
By default, Model tests with `hot-rod` profile spawn a new Infinispan container
with each test execution. It is also possible, to configure Model tests to
connect to an external instance of Infinispan. To do so, execute tests with
the following command:
```shell
mvn test -Phot-rod \
-Dkeycloak.testsuite.start-hotrod-container=false \
-Dkeycloak.connectionsHotRod.host=<host> \
-Dkeycloak.connectionsHotRod.port=<port> \
-Dkeycloak.connectionsHotRod.username=<username> \
-Dkeycloak.connectionsHotRod.password=<password>
```
Running tests with `map-jpa` profile using external Postgres database
---------------------------------------------
By default, Model tests with `map-jpa` profile spawns a new Postgres container
with each test execution. Default image used is "postgres:alpine". To spawn different
version, it can be used "keycloak.map.storage.postgres.docker.image" system property.
It is also possible, to configure Model tests to connect to an external instance
of Postgres. To do so, execute tests with the following command:
```shell
mvn test -Pmap-jpa \
-Dpostgres.start-container=false \
-Dkeycloak.map.storage.connectionsJpa.url=<jdbc_url> \
-Dkeycloak.map.storage.connectionsJpa.user=<user> \
-Dkeycloak.map.storage.connectionsJpa.password=<password>
```

View file

@ -29,7 +29,6 @@
<jdbc.mvn.version>${h2.version}</jdbc.mvn.version>
<log4j.configuration>file:${project.build.directory}/dependency/log4j.properties</log4j.configuration>
<jacoco.skip>true</jacoco.skip>
<keycloak.profile.feature.map_storage>disabled</keycloak.profile.feature.map_storage>
<keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase>false</keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase>
</properties>
@ -93,23 +92,11 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-infinispan</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-map</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-testsuite-providers</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-map-hot-rod</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-map-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core-jakarta</artifactId>
@ -119,18 +106,6 @@
<artifactId>postgresql</artifactId>
<version>${postgresql-jdbc.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<!-- annotations needed to avoid compile time warnings about enums constants -->
<dependency>
<groupId>org.infinispan</groupId>
@ -181,11 +156,7 @@
<keycloak.connectionsJpa.default.user>${keycloak.connectionsJpa.user}</keycloak.connectionsJpa.default.user>
<keycloak.connectionsJpa.default.password>${keycloak.connectionsJpa.password}</keycloak.connectionsJpa.default.password>
<keycloak.connectionsJpa.default.url>${keycloak.connectionsJpa.url}</keycloak.connectionsJpa.default.url>
<keycloak.map.storage.connectionsJpa.url>${keycloak.map.storage.connectionsJpa.url}</keycloak.map.storage.connectionsJpa.url>
<keycloak.map.storage.connectionsJpa.user>${keycloak.map.storage.connectionsJpa.user}</keycloak.map.storage.connectionsJpa.user>
<keycloak.map.storage.connectionsJpa.password>${keycloak.map.storage.connectionsJpa.password}</keycloak.map.storage.connectionsJpa.password>
<log4j.configuration>file:${project.build.directory}/test-classes/log4j.properties</log4j.configuration> <!-- for the logging to properly work with tests in the 'other' module -->
<keycloak.profile.feature.map_storage>${keycloak.profile.feature.map_storage}</keycloak.profile.feature.map_storage>
<keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase>${keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase}</keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<org.jboss.logging.provider>log4j</org.jboss.logging.provider>
@ -200,26 +171,6 @@
</properties>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-test-resources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<delete>
<fileset dir="${project.build.directory}" includes="map-*.json"/>
<fileset dir="${project.build.directory}" includes="map/**/*.json"/>
</delete>
</target>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
@ -341,46 +292,6 @@
</properties>
</profile>
<profile>
<id>map</id>
<properties>
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
<keycloak.model.parameters>Map,ConcurrentHashMapStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>file</id>
<properties>
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
<keycloak.model.parameters>Map,FileMapStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>hot-rod</id>
<properties>
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
<keycloak.model.parameters>Map,HotRodMapStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>map-ldap</id>
<properties>
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
<keycloak.model.parameters>Map,LdapMapStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>map-jpa</id>
<properties>
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
<keycloak.model.parameters>Map,JpaMapStorage</keycloak.model.parameters>
</properties>
</profile>
<profile>
<id>.asyncProfiler</id>
<activation>

View file

@ -1,206 +0,0 @@
/*
* 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.testsuite.model;
import org.hamcrest.Matchers;
import org.jboss.logging.Logger;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientProvider;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.map.client.MapClientEntity;
import org.keycloak.models.map.client.MapClientEntityImpl;
import org.keycloak.models.map.client.MapClientProviderFactory;
import org.keycloak.models.map.common.DeepCloner;
import org.keycloak.models.map.common.StringKeyConverter;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.MapStorageProvider;
import org.keycloak.models.map.storage.MapStorageProviderFactory;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorage;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.provider.InvalidationHandler;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.nullValue;
/**
*
* @author hmlnarik
*/
@RequireProvider(value = ClientProvider.class, only = {MapClientProviderFactory.PROVIDER_ID})
@RequireProvider(RealmProvider.class)
@RequireProvider(value = MapStorageProvider.class, only = {ConcurrentHashMapStorageProviderFactory.PROVIDER_ID})
public class ConcurrentHashMapStorageTest extends KeycloakModelTest {
private static final Logger LOG = Logger.getLogger(ConcurrentHashMapStorageTest.class.getName());
private String realmId;
private String mapStorageProviderId;
@Before
public void initMapStorageProviderId() {
MapStorageProviderFactory ms = (MapStorageProviderFactory) getFactory().getProviderFactory(MapStorageProvider.class, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID);
mapStorageProviderId = ms.getId();
assertThat(mapStorageProviderId, Matchers.notNullValue());
}
@Override
public void createEnvironment(KeycloakSession s) {
RealmModel realm = createRealm(s, "realm");
realm.setDefaultRole(s.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
this.realmId = realm.getId();
}
@Override
public void cleanEnvironment(KeycloakSession s) {
s.realms().removeRealm(realmId);
}
@Test
@SuppressWarnings("unchecked")
public <K, K1, K2> void testStorageSeparation() {
String component1Id = createMapStorageComponent("component1", "keyType", "ulong");
String component2Id = createMapStorageComponent("component2", "keyType", "string");
String[] ids = withRealm(realmId, (session, realm) -> {
ConcurrentHashMapStorage<K, MapClientEntity, ClientModel, ?> storageMain = (ConcurrentHashMapStorage<K, MapClientEntity, ClientModel, ?>) (MapStorage) session.getProvider(MapStorageProvider.class, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID).getMapStorage(ClientModel.class);
ConcurrentHashMapStorage<K1, MapClientEntity, ClientModel, ?> storage1 = (ConcurrentHashMapStorage<K1, MapClientEntity, ClientModel, ?>) (MapStorage) session.getComponentProvider(MapStorageProvider.class, component1Id).getMapStorage(ClientModel.class);
ConcurrentHashMapStorage<K2, MapClientEntity, ClientModel, ?> storage2 = (ConcurrentHashMapStorage<K2, MapClientEntity, ClientModel, ?>) (MapStorage) session.getComponentProvider(MapStorageProvider.class, component2Id).getMapStorage(ClientModel.class);
// Assert that the map storage can be used both as a standalone store and a component
assertThat(storageMain, notNullValue());
assertThat(storage1, notNullValue());
assertThat(storage2, notNullValue());
final StringKeyConverter<K> kcMain = (StringKeyConverter<K>) StringKeyConverter.UUIDKey.INSTANCE;
final StringKeyConverter<K1> kc1 = (StringKeyConverter<K1>) StringKeyConverter.ULongKey.INSTANCE;
final StringKeyConverter<K2> kc2 = (StringKeyConverter<K2>) StringKeyConverter.StringKey.INSTANCE;
String idMain = kcMain.keyToString(kcMain.yieldNewUniqueKey());
String id1 = kc1.keyToString(kc1.yieldNewUniqueKey());
String id2 = kc2.keyToString(kc2.yieldNewUniqueKey());
assertThat(idMain, notNullValue());
assertThat(id1, notNullValue());
assertThat(id2, notNullValue());
// Assert that the stores do not contain the to-be-created clients
assertThat(storageMain.read(idMain), nullValue());
assertThat(storage1.read(id1), nullValue());
assertThat(storage2.read(id2), nullValue());
assertClientDoesNotExist(storageMain, id1, kc1, kcMain);
assertClientDoesNotExist(storageMain, id2, kc2, kcMain);
assertClientDoesNotExist(storage1, idMain, kcMain, kc1);
assertClientDoesNotExist(storage1, id2, kc2, kc1);
assertClientDoesNotExist(storage2, idMain, kcMain, kc2);
assertClientDoesNotExist(storage2, id1, kc1, kc2);
MapClientEntity clientMain = new MapClientEntityImpl(DeepCloner.DUMB_CLONER);
clientMain.setId(idMain);
clientMain.setRealmId(realmId);
MapClientEntity client1 = new MapClientEntityImpl(DeepCloner.DUMB_CLONER);
client1.setId(id1);
client1.setRealmId(realmId);
MapClientEntity client2 = new MapClientEntityImpl(DeepCloner.DUMB_CLONER);
client2.setId(id2);
client2.setRealmId(realmId);
clientMain = storageMain.create(clientMain);
client1 = storage1.create(client1);
client2 = storage2.create(client2);
return new String[] {clientMain.getId(), client1.getId(), client2.getId()};
});
String idMain = ids[0];
String id1 = ids[1];
String id2 = ids[2];
LOG.debugf("Object IDs: %s, %s, %s", idMain, id1, id2);
assertClientsPersisted(component1Id, component2Id, idMain, id1, id2);
// Invalidate one component and check that the storage still contains what it should
getFactory().invalidate(null, InvalidationHandler.ObjectType.COMPONENT, component1Id);
assertClientsPersisted(component1Id, component2Id, idMain, id1, id2);
// Invalidate whole realm and check that the storage still contains what it should
getFactory().invalidate(null, InvalidationHandler.ObjectType.REALM, realmId);
assertClientsPersisted(component1Id, component2Id, idMain, id1, id2);
// Refresh factory (akin server restart) and check that the storage still contains what it should
reinitializeKeycloakSessionFactory();
assertClientsPersisted(component1Id, component2Id, idMain, id1, id2);
}
private <K,K1> void assertClientDoesNotExist(ConcurrentHashMapStorage<K, MapClientEntity, ClientModel, ?> storage, String id, final StringKeyConverter<K1> kc, final StringKeyConverter<K> kcStorage) {
// Assert that the other stores do not contain the to-be-created clients (if they use compatible key format)
try {
assertThat(storage.read(id), nullValue());
} catch (Exception ex) {
// If the format is incompatible then the object does not exist in the store
}
}
private <K, K1, K2> void assertClientsPersisted(String component1Id, String component2Id, String idMain, String id1, String id2) {
// Check that in the next transaction, the objects are still there
withRealm(realmId, (session, realm) -> {
@SuppressWarnings("unchecked")
ConcurrentHashMapStorage<K, MapClientEntity, ClientModel, ?> storageMain = (ConcurrentHashMapStorage<K, MapClientEntity, ClientModel, ?>) (MapStorage) session.getProvider(MapStorageProvider.class, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID).getMapStorage(ClientModel.class);
@SuppressWarnings("unchecked")
ConcurrentHashMapStorage<K1, MapClientEntity, ClientModel, ?> storage1 = (ConcurrentHashMapStorage<K1, MapClientEntity, ClientModel, ?>) (MapStorage) session.getComponentProvider(MapStorageProvider.class, component1Id).getMapStorage(ClientModel.class);
@SuppressWarnings("unchecked")
ConcurrentHashMapStorage<K2, MapClientEntity, ClientModel, ?> storage2 = (ConcurrentHashMapStorage<K2, MapClientEntity, ClientModel, ?>) (MapStorage) session.getComponentProvider(MapStorageProvider.class, component2Id).getMapStorage(ClientModel.class);
final StringKeyConverter<K> kcMain = (StringKeyConverter<K>) StringKeyConverter.UUIDKey.INSTANCE;
final StringKeyConverter<K1> kc1 = (StringKeyConverter<K1>) StringKeyConverter.ULongKey.INSTANCE;
final StringKeyConverter<K2> kc2 = (StringKeyConverter<K2>) StringKeyConverter.StringKey.INSTANCE;
// Assert that the stores contain the created clients
assertThat(storageMain.read(idMain), notNullValue());
assertThat(storage1.read(id1), notNullValue());
assertThat(storage2.read(id2), notNullValue());
// Assert that the other stores do not contain the to-be-created clients (if they use compatible key format)
assertClientDoesNotExist(storageMain, id1, kc1, kcMain);
assertClientDoesNotExist(storageMain, id2, kc2, kcMain);
assertClientDoesNotExist(storage1, idMain, kcMain, kc1);
assertClientDoesNotExist(storage1, id2, kc2, kc1);
assertClientDoesNotExist(storage2, idMain, kcMain, kc2);
assertClientDoesNotExist(storage2, id1, kc1, kc2);
assertThat(storageMain.read(idMain), notNullValue());
return null;
});
}
private String createMapStorageComponent(String name, String... config) {
ComponentModel c1 = KeycloakModelUtils.createComponentModel(name, realmId, mapStorageProviderId, MapStorageProvider.class.getName(), config);
return withRealm(realmId, (s, r) -> r.addComponentModel(c1).getId());
}
}

View file

@ -16,10 +16,7 @@
*/
package org.keycloak.testsuite.model.client;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
@ -28,23 +25,17 @@ import static org.hamcrest.Matchers.notNullValue;
import org.junit.Test;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientProvider;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleModel;
import org.keycloak.models.RoleProvider;
import org.keycloak.models.map.client.MapClientProvider;
import org.keycloak.models.map.client.MapClientProviderFactory;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
*
@ -154,23 +145,6 @@ public class ClientModelTest extends KeycloakModelTest {
}
}
@Test
@RequireProvider(value = ClientProvider.class, only = MapClientProviderFactory.PROVIDER_ID)
public void testDeleteClientUsingQueryParameters() {
final String CLIENT_ID = "createDeleteClientId";
// Create client
withRealm(realmId, (session, realm) -> session.clients().addClient(realm, CLIENT_ID));
// Check if exists
assertThat(withRealm(realmId, (session, realm) -> session.clients().getClientByClientId(realm, CLIENT_ID)), notNullValue());
// Remove
withRealm(realmId, (session, realm) -> {((MapClientProvider)session.clients()).preRemove(realm); return null;});
// Check is null
assertThat(withRealm(realmId, (session, realm) -> session.clients().getClientByClientId(realm, CLIENT_ID)), nullValue());
}
@Test
public void testScopeMappingRoleRemoval() {
// create two clients, one realm role and one client role and assign both to one of the clients

View file

@ -20,26 +20,17 @@ import org.keycloak.common.ClientConnection;
import org.keycloak.events.Event;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.EventStoreSpi;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
import org.keycloak.models.map.storage.file.FileMapStorageProviderFactory;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.junit.Assume.assumeFalse;
/**
*
@ -64,12 +55,6 @@ public class EventQueryTest extends KeycloakModelTest {
@Test
public void testClear() {
// Skip the test if EventProvider == File
String evProvider = CONFIG.getConfig().get(EventStoreSpi.NAME + ".provider");
String evMapStorageProvider = CONFIG.getConfig().get(EventStoreSpi.NAME + ".map.storage-auth-events.provider");
assumeFalse(MapEventStoreProviderFactory.PROVIDER_ID.equals(evProvider) &&
(evMapStorageProvider == null || FileMapStorageProviderFactory.PROVIDER_ID.equals(evMapStorageProvider)));
inRolledBackTransaction(null, (session, t) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
eventStore.clear();
@ -149,84 +134,6 @@ public class EventQueryTest extends KeycloakModelTest {
});
}
@Test
@RequireProvider(value = EventStoreProvider.class, only = "map")
public void testEventExpiration() {
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
// Set expiration so no event is valid
realm.setEventsExpiration(5);
Event e = createAuthEventForUser(session, realm, "u1");
eventStore.onEvent(e);
// Set expiration to 1000 seconds
realm.setEventsExpiration(1000);
e = createAuthEventForUser(session, realm, "u2");
eventStore.onEvent(e);
return null;
});
setTimeOffset(10);
try {
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
Set<Event> events = eventStore.createQuery()
.realm(realmId)
.getResultStream().collect(Collectors.toSet());
assertThat(events, hasSize(1));
assertThat(events.iterator().next().getUserId(), equalTo("u2"));
return null;
});
} finally {
setTimeOffset(0);
}
}
@Test
@RequireProvider(value = EventStoreProvider.class, only = "map")
public void testEventsClearedOnRealmRemoval() {
// Create another realm
String newRealmId = inComittedTransaction(null, (session, t) -> {
RealmModel realm = session.realms().createRealm("events-realm");
realm.setDefaultRole(session.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
Event e = createAuthEventForUser(session, realm, "u1");
eventStore.onEvent(e);
AdminEvent ae = new AdminEvent();
ae.setRealmId(realm.getId());
eventStore.onEvent(ae, false);
return realm.getId();
});
// Check if events were created
inComittedTransaction(session -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
assertThat(eventStore.createQuery().realm(newRealmId).getResultStream().count(), is(1L));
assertThat(eventStore.createAdminQuery().realm(newRealmId).getResultStream().count(), is(1L));
});
// Remove realm
inComittedTransaction((Consumer<KeycloakSession>) session -> session.realms().removeRealm(newRealmId));
// Check events were removed
inComittedTransaction(session -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
assertThat(eventStore.createQuery().realm(newRealmId).getResultStream().count(), is(0L));
assertThat(eventStore.createAdminQuery().realm(newRealmId).getResultStream().count(), is(0L));
});
}
private static class DummyClientConnection implements ClientConnection {
private static DummyClientConnection DUMMY_CONNECTION = new DummyClientConnection();

View file

@ -1,90 +0,0 @@
/*
* 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.testsuite.model.parameters;
import org.keycloak.common.crypto.CryptoIntegration;
import org.keycloak.common.crypto.CryptoProvider;
import org.keycloak.exportimport.ExportSpi;
import org.keycloak.exportimport.ImportSpi;
import org.keycloak.exportimport.dir.DirExportProviderFactory;
import org.keycloak.exportimport.dir.DirImportProviderFactory;
import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
import org.keycloak.exportimport.singlefile.SingleFileImportProviderFactory;
import org.keycloak.keys.KeyProviderFactory;
import org.keycloak.keys.KeySpi;
import org.keycloak.models.ClientScopeSpi;
import org.keycloak.models.map.storage.MapStorageSpi;
import org.keycloak.services.clientpolicy.ClientPolicyManagerFactory;
import org.keycloak.services.clientpolicy.ClientPolicyManagerSpi;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicySpi;
import org.keycloak.testsuite.model.KeycloakModelParameters;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
import org.keycloak.testsuite.model.Config;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
/**
*
* @author hmlnarik
*/
public class ConcurrentHashMapStorage extends KeycloakModelParameters {
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
.add(ExportSpi.class)
.add(ClientPolicyManagerSpi.class)
.add(ImportSpi.class)
.add(ClientRegistrationPolicySpi.class)
.add(ClientScopeSpi.class)
.add(KeySpi.class)
.build();
static {
// CryptoIntegration needed for import of realms
CryptoIntegration.init(CryptoProvider.class.getClassLoader());
}
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
.add(ConcurrentHashMapStorageProviderFactory.class)
// start providers needed for export
.add(SingleFileExportProviderFactory.class)
.add(DirExportProviderFactory.class)
.add(ClientPolicyManagerFactory.class)
// end providers needed for export
// start providers needed for import
.add(SingleFileImportProviderFactory.class)
.add(DirImportProviderFactory.class)
.add(ClientRegistrationPolicyFactory.class)
.add(KeyProviderFactory.class)
// end providers needed for import
.build();
@Override
public void updateConfig(Config cf) {
cf.spi(MapStorageSpi.NAME)
.defaultProvider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.config("dir", "${project.build.directory:target}");
}
public ConcurrentHashMapStorage() {
super(ALLOWED_SPIS, ALLOWED_FACTORIES);
}
}

View file

@ -1,89 +0,0 @@
/*
* Copyright 2023 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.model.parameters;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
import org.keycloak.authorization.store.StoreFactorySpi;
import org.keycloak.events.EventStoreSpi;
import org.keycloak.models.DeploymentStateSpi;
import org.keycloak.models.SingleUseObjectSpi;
import org.keycloak.models.UserLoginFailureSpi;
import org.keycloak.models.UserSessionSpi;
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
import org.keycloak.models.map.client.MapClientProviderFactory;
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
import org.keycloak.models.map.deploymentState.MapDeploymentStateProviderFactory;
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
import org.keycloak.models.map.group.MapGroupProviderFactory;
import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory;
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
import org.keycloak.models.map.realm.MapRealmProviderFactory;
import org.keycloak.models.map.role.MapRoleProviderFactory;
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
import org.keycloak.models.map.storage.MapStorageSpi;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
import org.keycloak.models.map.storage.file.FileMapStorageProviderFactory;
import org.keycloak.models.map.user.MapUserProviderFactory;
import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
import org.keycloak.sessions.AuthenticationSessionSpi;
import org.keycloak.testsuite.model.Config;
import org.keycloak.testsuite.model.KeycloakModelParameters;
public class FileMapStorage extends KeycloakModelParameters {
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
.build();
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
.add(FileMapStorageProviderFactory.class)
.add(ConcurrentHashMapStorageProviderFactory.class)
.build();
public FileMapStorage() {
super(ALLOWED_SPIS, ALLOWED_FACTORIES);
}
@Override
public void updateConfig(Config cf) {
cf.spi(MapStorageSpi.NAME)
.provider(FileMapStorageProviderFactory.PROVIDER_ID)
.config("dir", "${project.build.directory:target/file}")
.provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.config("dir", "${project.build.directory:target/chm}")
.spi(AuthenticationSessionSpi.PROVIDER_ID).provider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, FileMapStorageProviderFactory.PROVIDER_ID)
.spi("client").provider(MapClientProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, FileMapStorageProviderFactory.PROVIDER_ID)
.spi("clientScope").provider(MapClientScopeProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, FileMapStorageProviderFactory.PROVIDER_ID)
.spi("group").provider(MapGroupProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, FileMapStorageProviderFactory.PROVIDER_ID)
.spi("realm").provider(MapRealmProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, FileMapStorageProviderFactory.PROVIDER_ID)
.spi("role").provider(MapRoleProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, FileMapStorageProviderFactory.PROVIDER_ID)
.spi(DeploymentStateSpi.NAME).provider(MapDeploymentStateProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(StoreFactorySpi.NAME).provider(MapAuthorizationStoreFactory.PROVIDER_ID) .config(STORAGE_CONFIG, FileMapStorageProviderFactory.PROVIDER_ID)
.spi("user").provider(MapUserProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, FileMapStorageProviderFactory.PROVIDER_ID)
.spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, FileMapStorageProviderFactory.PROVIDER_ID)
.spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, FileMapStorageProviderFactory.PROVIDER_ID)
.spi(EventStoreSpi.NAME).provider(MapEventStoreProviderFactory.PROVIDER_ID) .config("storage-admin-events.provider", FileMapStorageProviderFactory.PROVIDER_ID)
.config("storage-auth-events.provider", FileMapStorageProviderFactory.PROVIDER_ID);
}
}

View file

@ -1,136 +0,0 @@
/*
* 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.testsuite.model.parameters;
import com.google.common.collect.ImmutableSet;
import org.jboss.logging.Logger;
import org.keycloak.authorization.store.StoreFactorySpi;
import org.keycloak.events.EventStoreSpi;
import org.keycloak.models.DeploymentStateSpi;
import org.keycloak.models.SingleUseObjectSpi;
import org.keycloak.models.UserLoginFailureSpi;
import org.keycloak.models.UserSessionSpi;
import org.keycloak.models.locking.GlobalLockProviderSpi;
import org.keycloak.models.locking.NoneGlobalLockProviderFactory;
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
import org.keycloak.models.map.client.MapClientProviderFactory;
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory;
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory;
import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionProviderFactory;
import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionSpi;
import org.keycloak.models.map.deploymentState.MapDeploymentStateProviderFactory;
import org.keycloak.models.map.group.MapGroupProviderFactory;
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
import org.keycloak.models.map.realm.MapRealmProviderFactory;
import org.keycloak.models.map.role.MapRoleProviderFactory;
import org.keycloak.models.map.storage.MapStorageSpi;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
import org.keycloak.models.map.storage.hotRod.HotRodMapStorageProviderFactory;
import org.keycloak.models.map.storage.hotRod.locking.HotRodGlobalLockProviderFactory;
import org.keycloak.models.map.user.MapUserProviderFactory;
import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
import org.keycloak.sessions.AuthenticationSessionSpi;
import org.keycloak.testsuite.model.Config;
import org.keycloak.testsuite.model.KeycloakModelParameters;
import org.keycloak.testsuite.util.InfinispanContainer;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import java.time.Duration;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.keycloak.testsuite.model.transaction.StorageTransactionTest.LOCK_TIMEOUT_SYSTEM_PROPERTY;
/**
*
* @author hmlnarik
*/
public class HotRodMapStorage extends KeycloakModelParameters {
private final Logger LOG = Logger.getLogger(getClass());
public static final Boolean START_CONTAINER = Boolean.valueOf(System.getProperty("keycloak.testsuite.start-hotrod-container", "true"));
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
.add(HotRodConnectionSpi.class)
.build();
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
.add(HotRodMapStorageProviderFactory.class)
.add(HotRodConnectionProviderFactory.class)
.add(HotRodGlobalLockProviderFactory.class)
.build();
private final InfinispanContainer hotRodContainer = new InfinispanContainer();
@Override
public void updateConfig(Config cf) {
cf.spi(AuthenticationSessionSpi.PROVIDER_ID).provider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi("client").provider(MapClientProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi("clientScope").provider(MapClientScopeProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi("group").provider(MapGroupProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi("realm").provider(MapRealmProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi("role").provider(MapRoleProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi(DeploymentStateSpi.NAME).provider(MapDeploymentStateProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(StoreFactorySpi.NAME).provider(MapAuthorizationStoreFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi("user").provider(MapUserProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi(EventStoreSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID).config("storage-admin-events.provider", HotRodMapStorageProviderFactory.PROVIDER_ID)
.config("storage-auth-events.provider", HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi(GlobalLockProviderSpi.GLOBAL_LOCK).defaultProvider(HotRodGlobalLockProviderFactory.PROVIDER_ID);
cf.spi(MapStorageSpi.NAME)
.provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.config("dir", "${project.build.directory:target}")
.config("keyType.single-use-objects", "string");
cf.spi(HotRodConnectionSpi.NAME).provider(DefaultHotRodConnectionProviderFactory.PROVIDER_ID)
.config("host", hotRodContainer.getHost())
.config("port", hotRodContainer.getPort())
.config("username", hotRodContainer.getUsername())
.config("password", hotRodContainer.getPassword())
.config("configureRemoteCaches", "true")
.config("lockTimeout", "${" + LOCK_TIMEOUT_SYSTEM_PROPERTY + ":}");
}
@Override
public void beforeSuite(Config cf) {
if (START_CONTAINER) {
hotRodContainer.start();
}
}
@Override
public void afterSuite() {
if (START_CONTAINER) {
hotRodContainer.stop();
}
}
public HotRodMapStorage() {
super(ALLOWED_SPIS, ALLOWED_FACTORIES);
}
}

View file

@ -1,143 +0,0 @@
/*
* 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.testsuite.model.parameters;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
import org.jboss.logging.Logger;
import org.keycloak.authorization.store.StoreFactorySpi;
import org.keycloak.events.EventStoreSpi;
import org.keycloak.models.DeploymentStateSpi;
import org.keycloak.models.SingleUseObjectSpi;
import org.keycloak.models.UserLoginFailureSpi;
import org.keycloak.models.UserSessionSpi;
import org.keycloak.models.locking.GlobalLockProviderSpi;
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
import org.keycloak.models.map.client.MapClientProviderFactory;
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
import org.keycloak.models.map.deploymentState.MapDeploymentStateProviderFactory;
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
import org.keycloak.models.map.group.MapGroupProviderFactory;
import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory;
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
import org.keycloak.models.map.realm.MapRealmProviderFactory;
import org.keycloak.models.map.role.MapRoleProviderFactory;
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
import org.keycloak.models.map.storage.MapStorageSpi;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
import org.keycloak.models.map.lock.MapGlobalLockProviderFactory;
import org.keycloak.models.map.storage.jpa.JpaMapStorageProviderFactory;
import org.keycloak.models.map.storage.jpa.liquibase.connection.MapLiquibaseConnectionProviderFactory;
import org.keycloak.models.map.storage.jpa.liquibase.connection.MapLiquibaseConnectionSpi;
import org.keycloak.models.map.storage.jpa.updater.MapJpaUpdaterProviderFactory;
import org.keycloak.models.map.storage.jpa.updater.MapJpaUpdaterSpi;
import org.keycloak.models.map.user.MapUserProviderFactory;
import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
import org.keycloak.sessions.AuthenticationSessionSpi;
import org.keycloak.testsuite.model.Config;
import org.keycloak.testsuite.model.KeycloakModelParameters;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName;
import static org.keycloak.testsuite.model.transaction.StorageTransactionTest.LOCK_TIMEOUT_SYSTEM_PROPERTY;
public class JpaMapStorage extends KeycloakModelParameters {
private static final Logger LOG = Logger.getLogger(JpaMapStorage.class.getName());
private static final Boolean START_CONTAINER = Boolean.valueOf(System.getProperty("postgres.start-container", "true"));
private static final String POSTGRES_DOCKER_IMAGE_NAME = System.getProperty("keycloak.map.storage.postgres.docker.image", "postgres:alpine");
private static final PostgreSQLContainer POSTGRES_CONTAINER = new PostgreSQLContainer(DockerImageName.parse(POSTGRES_DOCKER_IMAGE_NAME).asCompatibleSubstituteFor("postgres"));
private static final String POSTGRES_DB_DEFAULT_NAME = System.getProperty("keycloak.map.storage.connectionsJpa.databaseName", "keycloak");
private static final String POSTGRES_DB_USER = System.getProperty("keycloak.map.storage.connectionsJpa.user", "keycloak");
private static final String POSTGRES_DB_PASSWORD = System.getProperty("keycloak.map.storage.connectionsJpa.password", "pass");
private static String POSTGRES_DB_JDBC_URL = System.getProperty("keycloak.map.storage.connectionsJpa.url");
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
.add(MapJpaUpdaterSpi.class)
.add(MapLiquibaseConnectionSpi.class)
.build();
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
.add(ConcurrentHashMapStorageProviderFactory.class)
.add(JpaMapStorageProviderFactory.class)
.add(MapJpaUpdaterProviderFactory.class)
.add(MapLiquibaseConnectionProviderFactory.class)
.add(MapGlobalLockProviderFactory.class)
.build();
public JpaMapStorage() {
super(ALLOWED_SPIS, ALLOWED_FACTORIES);
}
@Override
public void updateConfig(Config cf) {
cf.spi(MapStorageSpi.NAME)
.provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.config("dir", "${project.build.directory:target}");
cf.spi(MapStorageSpi.NAME)
.provider(JpaMapStorageProviderFactory.PROVIDER_ID)
.config("url", POSTGRES_DB_JDBC_URL)
.config("user", POSTGRES_DB_USER)
.config("password", POSTGRES_DB_PASSWORD)
.config("driver", "org.postgresql.Driver")
.config("lockTimeout", "${" + LOCK_TIMEOUT_SYSTEM_PROPERTY + ":}");
cf.spi(AuthenticationSessionSpi.PROVIDER_ID).provider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("client").provider(MapClientProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("clientScope").provider(MapClientScopeProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("group").provider(MapGroupProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("realm").provider(MapRealmProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("role").provider(MapRoleProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi(DeploymentStateSpi.NAME).provider(MapDeploymentStateProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(StoreFactorySpi.NAME).provider(MapAuthorizationStoreFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("user").provider(MapUserProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi(EventStoreSpi.NAME).provider(MapEventStoreProviderFactory.PROVIDER_ID) .config("storage-admin-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID)
.config("storage-auth-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID)
.spi(GlobalLockProviderSpi.GLOBAL_LOCK) .config("provider", MapGlobalLockProviderFactory.PROVIDER_ID)
.spi(GlobalLockProviderSpi.GLOBAL_LOCK).provider(MapGlobalLockProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID);
}
@Override
public void beforeSuite(Config cf) {
if (START_CONTAINER) {
POSTGRES_CONTAINER
.withDatabaseName(POSTGRES_DB_DEFAULT_NAME)
.withUsername(POSTGRES_DB_USER)
.withPassword(POSTGRES_DB_PASSWORD)
.start();
POSTGRES_DB_JDBC_URL = POSTGRES_CONTAINER.getJdbcUrl();
}
}
@Override
public void afterSuite() {
if (START_CONTAINER) {
POSTGRES_CONTAINER.stop();
}
}
}

View file

@ -1,145 +0,0 @@
/*
* Copyright 2022 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.model.parameters;
import com.google.common.collect.ImmutableSet;
import org.keycloak.authorization.store.StoreFactorySpi;
import org.keycloak.events.EventStoreSpi;
import org.keycloak.models.DeploymentStateSpi;
import org.keycloak.models.SingleUseObjectSpi;
import org.keycloak.models.UserLoginFailureSpi;
import org.keycloak.models.UserSessionSpi;
import org.keycloak.models.locking.GlobalLockProviderSpi;
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
import org.keycloak.models.map.client.MapClientProviderFactory;
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
import org.keycloak.models.map.deploymentState.MapDeploymentStateProviderFactory;
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
import org.keycloak.models.map.group.MapGroupProviderFactory;
import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory;
import org.keycloak.models.map.lock.MapGlobalLockProviderFactory;
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
import org.keycloak.models.map.realm.MapRealmProviderFactory;
import org.keycloak.models.map.role.MapRoleProviderFactory;
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
import org.keycloak.models.map.storage.MapStorageSpi;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
import org.keycloak.models.map.storage.jpa.JpaMapStorageProviderFactory;
import org.keycloak.models.map.storage.jpa.liquibase.connection.MapLiquibaseConnectionProviderFactory;
import org.keycloak.models.map.storage.jpa.liquibase.connection.MapLiquibaseConnectionSpi;
import org.keycloak.models.map.storage.jpa.updater.MapJpaUpdaterProviderFactory;
import org.keycloak.models.map.storage.jpa.updater.MapJpaUpdaterSpi;
import org.keycloak.models.map.user.MapUserProviderFactory;
import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
import org.keycloak.sessions.AuthenticationSessionSpi;
import org.keycloak.testsuite.model.Config;
import org.keycloak.testsuite.model.KeycloakModelParameters;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.testcontainers.containers.CockroachContainer;
import org.testcontainers.utility.DockerImageName;
import java.util.Set;
import static org.keycloak.testsuite.model.transaction.StorageTransactionTest.LOCK_TIMEOUT_SYSTEM_PROPERTY;
public class JpaMapStorageCockroachdb extends KeycloakModelParameters {
private static final Boolean START_CONTAINER = Boolean.valueOf(System.getProperty("cockroachdb.start-container", "true"));
private static final String COCKROACHDB_DOCKER_IMAGE_NAME = System.getProperty("keycloak.map.storage.cockroachdb.docker.image", "cockroachdb/cockroach:v22.1.0");
private static final CockroachContainer COCKROACHDB_CONTAINER = new CockroachContainer(DockerImageName.parse(COCKROACHDB_DOCKER_IMAGE_NAME).asCompatibleSubstituteFor("cockroachdb"));
private static final String COCKROACHDB_DB_USER = System.getProperty("keycloak.map.storage.connectionsJpa.user", "keycloak");
private static final String COCKROACHDB_DB_PASSWORD = System.getProperty("keycloak.map.storage.connectionsJpa.password", "pass");
private static String COCKROACHDB_DB_JDBC_URL = System.getProperty("keycloak.map.storage.connectionsJpa.url");
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
.add(MapJpaUpdaterSpi.class)
.add(MapLiquibaseConnectionSpi.class)
.build();
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
.add(ConcurrentHashMapStorageProviderFactory.class)
.add(JpaMapStorageProviderFactory.class)
.add(MapJpaUpdaterProviderFactory.class)
.add(MapLiquibaseConnectionProviderFactory.class)
.add(MapGlobalLockProviderFactory.class)
.build();
public JpaMapStorageCockroachdb() {
super(ALLOWED_SPIS, ALLOWED_FACTORIES);
}
@Override
public void updateConfig(Config cf) {
cf.spi(MapStorageSpi.NAME)
.provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.config("dir", "${project.build.directory:target}");
cf.spi(MapStorageSpi.NAME)
.provider(JpaMapStorageProviderFactory.PROVIDER_ID)
.config("url", COCKROACHDB_DB_JDBC_URL)
.config("user", COCKROACHDB_DB_USER)
.config("password", COCKROACHDB_DB_PASSWORD)
.config("driver", "org.postgresql.Driver")
.config("lockTimeout", "${" + LOCK_TIMEOUT_SYSTEM_PROPERTY + ":}");
cf.spi(AuthenticationSessionSpi.PROVIDER_ID).provider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("client").provider(MapClientProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("clientScope").provider(MapClientScopeProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("group").provider(MapGroupProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("realm").provider(MapRealmProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("role").provider(MapRoleProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi(DeploymentStateSpi.NAME).provider(MapDeploymentStateProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(StoreFactorySpi.NAME).provider(MapAuthorizationStoreFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("user").provider(MapUserProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
.spi(EventStoreSpi.NAME).provider(MapEventStoreProviderFactory.PROVIDER_ID) .config("storage-admin-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID)
.config("storage-auth-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID)
.spi(GlobalLockProviderSpi.GLOBAL_LOCK) .config("provider", MapGlobalLockProviderFactory.PROVIDER_ID)
.spi(GlobalLockProviderSpi.GLOBAL_LOCK).provider(MapGlobalLockProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID);
}
@Override
public void beforeSuite(Config cf) {
if (START_CONTAINER) {
COCKROACHDB_CONTAINER
// Using the environment variables for now where using the withXXX() method is not supported, yet.
// https://github.com/testcontainers/testcontainers-java/issues/6299
.withEnv("COCKROACH_DATABASE", "keycloak")
.withEnv("COCKROACH_USER", COCKROACHDB_DB_USER)
// password is not used/supported in insecure mode
.withCommand("start-single-node", "--insecure")
.start();
COCKROACHDB_DB_JDBC_URL = COCKROACHDB_CONTAINER.getJdbcUrl();
}
System.setProperty(KeycloakModelTest.KEYCLOAK_MODELTESTS_RETRY_TRANSACTIONS, "true");
}
@Override
public void afterSuite() {
if (START_CONTAINER) {
COCKROACHDB_CONTAINER.stop();
}
}
}

View file

@ -1,119 +0,0 @@
/*
* 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.testsuite.model.parameters;
import com.google.common.collect.ImmutableSet;
import org.jboss.logging.Logger;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.keycloak.authorization.store.StoreFactorySpi;
import org.keycloak.events.EventStoreSpi;
import org.keycloak.models.DeploymentStateSpi;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.SingleUseObjectSpi;
import org.keycloak.models.UserLoginFailureSpi;
import org.keycloak.models.UserSessionSpi;
import org.keycloak.models.map.storage.MapStorageSpi;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
import org.keycloak.models.map.storage.ldap.LdapMapStorageProviderFactory;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
import org.keycloak.testsuite.model.Config;
import org.keycloak.testsuite.model.KeycloakModelParameters;
import org.keycloak.testsuite.util.LDAPRule;
import org.keycloak.util.ldap.LDAPEmbeddedServer;
import java.util.Set;
/**
* @author Alexander Schwartz
*/
public class LdapMapStorage extends KeycloakModelParameters {
private static final Logger LOG = Logger.getLogger(LdapMapStorage.class.getName());
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
.build();
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
.add(ConcurrentHashMapStorageProviderFactory.class)
.add(LdapMapStorageProviderFactory.class)
.build();
private final LDAPRule ldapRule = new LDAPRule();
public LdapMapStorage() {
super(ALLOWED_SPIS, ALLOWED_FACTORIES);
}
@Override
public void updateConfig(Config cf) {
cf.spi(MapStorageSpi.NAME)
.provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.config("dir", "${project.build.directory:target}");
cf.spi(MapStorageSpi.NAME)
.provider(LdapMapStorageProviderFactory.PROVIDER_ID)
.config("vendor", "other")
.config("usernameLDAPAttribute", "uid")
.config("rdnLDAPAttribute", "uid")
.config("uuidLDAPAttribute", "entryUUID")
.config("userObjectClasses", "inetOrgPerson, organizationalPerson")
.config("connectionUrl", "ldap://localhost:10389")
.config("usersDn", "ou=People,dc=keycloak,dc=org")
.config("bindDn", "uid=admin,ou=system")
.config("bindCredential", "secret")
.config("roles.realm.dn", "ou=RealmRoles,dc=keycloak,dc=org")
.config("roles.client.dn", "ou={0},dc=keycloak,dc=org")
.config("roles.common.dn", "dc=keycloak,dc=org") // this is the top DN that finds both client and realm roles
.config("membership.ldap.attribute", "member")
.config("role.name.ldap.attribute", "cn")
.config("role.object.classes", "groupOfNames")
.config("role.attributes", "ou")
.config("mode", "LDAP_ONLY")
.config("use.realm.roles.mapping", "true")
.config(LDAPConstants.CONNECTION_POOLING, "true");
cf.spi("client").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi("clientScope").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi("group").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi("realm").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi("role").config("map.storage.provider", LdapMapStorageProviderFactory.PROVIDER_ID)
.spi(DeploymentStateSpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(StoreFactorySpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi("user").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(UserSessionSpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(UserLoginFailureSpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi("authorizationPersister").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi("authenticationSessions").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(EventStoreSpi.NAME).config("map.storage-admin-events.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(EventStoreSpi.NAME).config("map.storage-auth-events.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(SingleUseObjectSpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi("publicKeyStorage").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID);
}
static {
System.setProperty(LDAPEmbeddedServer.PROPERTY_ENABLE_SSL, "false");
}
@Override
public Statement classRule(Statement base, Description description) {
return ldapRule.apply(base, description);
}
}

View file

@ -1,109 +0,0 @@
/*
* 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.testsuite.model.parameters;
import org.keycloak.authorization.store.StoreFactorySpi;
import org.keycloak.events.EventStoreSpi;
import org.keycloak.keys.PublicKeyStorageSpi;
import org.keycloak.models.DeploymentStateSpi;
import org.keycloak.models.SingleUseObjectProviderFactory;
import org.keycloak.models.SingleUseObjectSpi;
import org.keycloak.models.UserLoginFailureSpi;
import org.keycloak.models.UserSessionSpi;
import org.keycloak.models.locking.GlobalLockProviderSpi;
import org.keycloak.models.locking.NoneGlobalLockProviderFactory;
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory;
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
import org.keycloak.sessions.AuthenticationSessionSpi;
import org.keycloak.testsuite.model.KeycloakModelParameters;
import org.keycloak.models.map.client.MapClientProviderFactory;
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
import org.keycloak.models.map.group.MapGroupProviderFactory;
import org.keycloak.models.map.realm.MapRealmProviderFactory;
import org.keycloak.models.map.role.MapRoleProviderFactory;
import org.keycloak.models.map.deploymentState.MapDeploymentStateProviderFactory;
import org.keycloak.models.map.storage.MapStorageSpi;
import org.keycloak.models.map.user.MapUserProviderFactory;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
import org.keycloak.testsuite.model.Config;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
/**
*
* @author hmlnarik
*/
public class Map extends KeycloakModelParameters {
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
.add(AuthenticationSessionSpi.class)
.add(SingleUseObjectSpi.class)
.add(PublicKeyStorageSpi.class)
.add(MapStorageSpi.class)
.build();
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
.add(MapAuthorizationStoreFactory.class)
.add(MapClientProviderFactory.class)
.add(MapClientScopeProviderFactory.class)
.add(MapGroupProviderFactory.class)
.add(MapRealmProviderFactory.class)
.add(MapRoleProviderFactory.class)
.add(MapRootAuthenticationSessionProviderFactory.class)
.add(MapDeploymentStateProviderFactory.class)
.add(MapUserProviderFactory.class)
.add(MapUserSessionProviderFactory.class)
.add(MapUserLoginFailureProviderFactory.class)
.add(NoneGlobalLockProviderFactory.class)
.add(MapEventStoreProviderFactory.class)
.add(SingleUseObjectProviderFactory.class)
.add(MapPublicKeyStorageProviderFactory.class)
.build();
public Map() {
super(ALLOWED_SPIS, ALLOWED_FACTORIES);
}
@Override
public void updateConfig(Config cf) {
cf.spi(AuthenticationSessionSpi.PROVIDER_ID).defaultProvider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID)
.spi(SingleUseObjectSpi.NAME).defaultProvider(MapSingleUseObjectProviderFactory.PROVIDER_ID)
.spi("client").defaultProvider(MapClientProviderFactory.PROVIDER_ID)
.spi("clientScope").defaultProvider(MapClientScopeProviderFactory.PROVIDER_ID)
.spi("group").defaultProvider(MapGroupProviderFactory.PROVIDER_ID)
.spi("realm").defaultProvider(MapRealmProviderFactory.PROVIDER_ID)
.spi("role").defaultProvider(MapRoleProviderFactory.PROVIDER_ID)
.spi(DeploymentStateSpi.NAME).defaultProvider(MapDeploymentStateProviderFactory.PROVIDER_ID)
.spi(StoreFactorySpi.NAME).defaultProvider(MapAuthorizationStoreFactory.PROVIDER_ID)
.spi("user").defaultProvider(MapUserProviderFactory.PROVIDER_ID)
.spi(UserSessionSpi.NAME).defaultProvider(MapUserSessionProviderFactory.PROVIDER_ID)
.spi(UserLoginFailureSpi.NAME).defaultProvider(MapUserLoginFailureProviderFactory.PROVIDER_ID)
.spi(GlobalLockProviderSpi.GLOBAL_LOCK).defaultProvider(NoneGlobalLockProviderFactory.PROVIDER_ID)
.spi(EventStoreSpi.NAME).defaultProvider(MapEventStoreProviderFactory.PROVIDER_ID)
.spi("publicKeyStorage").defaultProvider(MapPublicKeyStorageProviderFactory.PROVIDER_ID)
;
cf.spi(MapStorageSpi.NAME).provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID).config("keyType.single-use-objects", "string");
}
}

View file

@ -1,180 +0,0 @@
/*
* Copyright 2022 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.model.session;
import org.infinispan.client.hotrod.RemoteCache;
import org.junit.Test;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.map.storage.ModelEntityUtil;
import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory;
import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionProvider;
import org.keycloak.models.map.storage.hotRod.userSession.HotRodUserSessionEntity;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.aMapWithSize;
import static org.hamcrest.Matchers.anEmptyMap;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.notNullValue;
import static org.keycloak.protocol.oidc.OIDCConfigAttributes.CLIENT_SESSION_IDLE_TIMEOUT;
import static org.keycloak.protocol.oidc.OIDCConfigAttributes.CLIENT_SESSION_MAX_LIFESPAN;
@RequireProvider(UserSessionProvider.class)
@RequireProvider(value = HotRodConnectionProvider.class, only = DefaultHotRodConnectionProviderFactory.PROVIDER_ID)
public class HotRodUserSessionClientSessionRelationshipTest extends KeycloakModelTest {
private String realmId;
private String CLIENT0_CLIENT_ID = "client0";
@Override
public void createEnvironment(KeycloakSession s) {
RealmModel realm = createRealm(s, "test");
realm.setDefaultRole(s.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
realm.setSsoSessionIdleTimeout(1800);
realm.setSsoSessionMaxLifespan(36000);
this.realmId = realm.getId();
s.clients().addClient(realm, CLIENT0_CLIENT_ID);
s.users().addUser(realm, "user1").setEmail("user1@localhost");
}
@Override
public void cleanEnvironment(KeycloakSession s) {
if (realmId != null) {
s.realms().removeRealm(realmId);
}
}
@Test
public void testClientSessionAreRemovedOnUserSessionRemoval() {
AtomicReference<String> uSessionId = new AtomicReference<>();
AtomicReference<String> cSessionId = new AtomicReference<>();
prepareSessions(uSessionId, cSessionId);
withRealm(realmId, (session, realm) -> {
UserSessionModel uSession = session.sessions().getUserSession(realm, uSessionId.get());
session.sessions().removeUserSession(realm, uSession);
return null;
});
assertCacheContains(remoteCache -> assertThat(remoteCache, anEmptyMap()));
}
@Test
public void testSessionsAreRemovedOnUserRemoval() {
AtomicReference<String> uSessionId = new AtomicReference<>();
AtomicReference<String> cSessionId = new AtomicReference<>();
prepareSessions(uSessionId, cSessionId);
withRealm(realmId, (session, realm) -> {
session.users().removeUser(realm, session.users().getUserByUsername(realm, "user1"));
return null;
});
assertCacheContains(remoteCache -> {
assertThat(remoteCache, anEmptyMap());
});
}
@Test
public void testSessionsAreRemovedOnRealmRemoval() {
AtomicReference<String> uSessionId = new AtomicReference<>();
AtomicReference<String> cSessionId = new AtomicReference<>();
prepareSessions(uSessionId, cSessionId);
withRealm(realmId, (session, realm) -> {
session.realms().removeRealm(realm.getId());
return null;
});
assertCacheContains(remoteCache -> {
assertThat(remoteCache, anEmptyMap());
});
}
@Test
public void testExpiredClientSessionReferenceIsNotPresentInUserSession() {
// Set lower client session timeouts
withRealm(realmId, (session, realm) -> {
ClientModel client = realm.getClientByClientId(CLIENT0_CLIENT_ID);
client.setAttribute(CLIENT_SESSION_IDLE_TIMEOUT, "60");
client.setAttribute(CLIENT_SESSION_MAX_LIFESPAN, "65");
return null;
});
AtomicReference<String> uSessionId = new AtomicReference<>();
AtomicReference<String> cSessionId = new AtomicReference<>();
prepareSessions(uSessionId, cSessionId);
// Move in time when client session should be expired but user session not
setTimeOffset(70);
// Try to create a new client session for the same user session
withRealm(realmId, (session, realm) -> {
ClientModel client = realm.getClientByClientId(CLIENT0_CLIENT_ID);
UserSessionModel uSession = session.sessions().getUserSession(realm, uSessionId.get());
assertThat(uSession.getAuthenticatedClientSessions(), anEmptyMap());
assertThat(session.sessions().createClientSession(realm, client, uSession), notNullValue());
return null;
});
// Check session does not contain a reference to expired client session
assertCacheContains(remoteCache -> {
HotRodUserSessionEntity hotRodUserSessionEntity = remoteCache.get(uSessionId.get());
assertThat(hotRodUserSessionEntity.authenticatedClientSessions, hasSize(1));
});
}
private void assertCacheContains(Consumer<RemoteCache<String, HotRodUserSessionEntity>> checker) {
withRealm(realmId, (session, realm) -> {
HotRodConnectionProvider provider = session.getProvider(HotRodConnectionProvider.class);
RemoteCache<String, HotRodUserSessionEntity> remoteCache = provider.getRemoteCache(ModelEntityUtil.getModelName(UserSessionModel.class));
checker.accept(remoteCache);
return null;
});
}
private void prepareSessions(AtomicReference<String> uSessionId, AtomicReference<String> cSessionId) {
withRealm(realmId, (session, realm) -> {
UserSessionModel uSession = session.sessions().createUserSession(null, realm, session.users().getUserByUsername(realm, "user1"), "user1", "127.0.0.1", "form", true, null, null, UserSessionModel.SessionPersistenceState.PERSISTENT);
ClientModel client = realm.getClientByClientId(CLIENT0_CLIENT_ID);
AuthenticatedClientSessionModel cSession = session.sessions().createClientSession(realm, client, uSession);
uSessionId.set(uSession.getId());
cSessionId.set(cSession.getId());
return null;
});
assertCacheContains(remoteCache -> {
assertThat(remoteCache, aMapWithSize(2));
assertThat(remoteCache.keySet(), containsInAnyOrder(uSessionId.get(), cSessionId.get()));
});
}
}

View file

@ -16,7 +16,6 @@
*/
package org.keycloak.testsuite.model.session;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.commons.CacheException;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
@ -28,13 +27,8 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.map.storage.ModelEntityUtil;
import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory;
import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionProvider;
import org.keycloak.models.map.storage.hotRod.userSession.HotRodUserSessionEntity;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProvider;
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProviderFactory;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
@ -131,39 +125,6 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
}
}
@Test
@RequireProvider(value = HotRodConnectionProvider.class, only = DefaultHotRodConnectionProviderFactory.PROVIDER_ID)
public void testOfflineSessionsRemovedAfterDeleteRealm() {
String realmId2 = inComittedTransaction(session -> { return prepareRealm(session, "realm2").getId(); });
List<String> userIds2 = withRealm(realmId2, (session, realm) -> IntStream.range(0, USER_COUNT)
.mapToObj(i -> session.users().addUser(realm, "user2-" + i))
.map(UserModel::getId)
.collect(Collectors.toList())
);
try {
List<String> offlineSessionIds2 = createOfflineSessions(realmId2, userIds2);
assertOfflineSessionsExist(realmId2, offlineSessionIds2);
// Simulate server restart
reinitializeKeycloakSessionFactory();
assertOfflineSessionsExist(realmId2, offlineSessionIds2);
inComittedTransaction(session -> {
session.realms().removeRealm(realmId2);
});
inComittedTransaction(session -> {
HotRodConnectionProvider provider = session.getProvider(HotRodConnectionProvider.class);
RemoteCache<String, HotRodUserSessionEntity> remoteCache = provider.getRemoteCache(ModelEntityUtil.getModelName(UserSessionModel.class));
assertThat(remoteCache, Matchers.anEmptyMap());
});
} finally {
withRealm(realmId2, (session, realm) -> realm == null ? false : new RealmManager(session).removeRealm(realm));
}
}
@Test
public void testPersistenceSingleNode() {
List<String> offlineSessionIds = createOfflineSessions(realmId, userIds);
@ -176,7 +137,6 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
@Test(timeout = 90 * 1000)
@RequireProvider(UserSessionPersisterProvider.class)
@RequireProvider(value = UserSessionProvider.class, only = InfinispanUserSessionProviderFactory.PROVIDER_ID)
public void testPersistenceMultipleNodesClientSessionAtSameNode() throws InterruptedException {
int numClients = 2;
List<String> clientIds = withRealm(realmId, (session, realm) -> IntStream.range(0, numClients)
@ -233,7 +193,6 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
@Test(timeout = 90 * 1000)
@RequireProvider(UserSessionPersisterProvider.class)
@RequireProvider(value = UserSessionProvider.class, only = InfinispanUserSessionProviderFactory.PROVIDER_ID)
public void testPersistenceMultipleNodesClientSessionsAtRandomNode() throws InterruptedException {
List<String> clientIds = withRealm(realmId, (session, realm) -> IntStream.range(0, 5)
.mapToObj(cid -> session.clients().addClient(realm, "client-" + cid))
@ -286,7 +245,6 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
@Test
@RequireProvider(UserSessionPersisterProvider.class)
@RequireProvider(value = UserSessionProvider.class, only = InfinispanUserSessionProviderFactory.PROVIDER_ID)
public void testOfflineSessionLoadingAfterCacheRemoval() {
List<String> offlineSessionIds = createOfflineSessions(realmId, userIds);
assertOfflineSessionsExist(realmId, offlineSessionIds);
@ -311,7 +269,6 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
@Test
@RequireProvider(UserSessionPersisterProvider.class)
@RequireProvider(value = UserSessionProvider.class, only = InfinispanUserSessionProviderFactory.PROVIDER_ID)
public void testLazyClientSessionStatsFetching() {
List<String> clientIds = withRealm(realmId, (session, realm) -> IntStream.range(0, 5)
.mapToObj(cid -> session.clients().addClient(realm, "client-" + cid))
@ -346,7 +303,6 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
@Test
@RequireProvider(UserSessionPersisterProvider.class)
@RequireProvider(value = UserSessionProvider.class, only = InfinispanUserSessionProviderFactory.PROVIDER_ID)
public void testLazyOfflineUserSessionFetching() {
Map<String, Set<String>> offlineSessionIdsDetailed = createOfflineSessionsDetailed(realmId, userIds);
Collection<String> offlineSessionIds = offlineSessionIdsDetailed.values().stream().flatMap(Set::stream).collect(Collectors.toCollection(TreeSet::new));
@ -380,7 +336,6 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
@Test(timeout = 90 * 1000)
@RequireProvider(UserSessionPersisterProvider.class)
@RequireProvider(value = UserSessionProvider.class, only = InfinispanUserSessionProviderFactory.PROVIDER_ID)
public void testPersistenceClientSessionsMultipleNodes() throws InterruptedException {
// Create offline sessions
List<String> offlineSessionIds = createOfflineSessions(realmId, userIds);

View file

@ -25,17 +25,10 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.UserSessionSpi;
import org.keycloak.models.map.storage.ModelEntityUtil;
import org.keycloak.models.map.storage.file.FileMapStorageProviderFactory;
import org.keycloak.models.map.storage.hotRod.HotRodMapStorageProviderFactory;
import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionProvider;
import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@ -43,9 +36,7 @@ import java.util.stream.IntStream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.aMapWithSize;
import static org.hamcrest.Matchers.anEmptyMap;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assume.assumeFalse;
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
@ -56,7 +47,6 @@ public class UserSessionConcurrencyTest extends KeycloakModelTest {
private static final int CLIENTS_COUNT = 10;
private static final ThreadLocal<Boolean> wasWriting = ThreadLocal.withInitial(() -> false);
private final boolean isHotRodStore = HotRodMapStorageProviderFactory.PROVIDER_ID.equals(CONFIG.getConfig().get("userSessions.map.storage.provider"));
@Override
public void createEnvironment(KeycloakSession s) {
@ -87,13 +77,6 @@ public class UserSessionConcurrencyTest extends KeycloakModelTest {
@Test
public void testConcurrentNotesChange() throws InterruptedException {
// Defer this one until file locking is available
// Skip the test if EventProvider == File
String evProvider = CONFIG.getConfig().get(UserSessionSpi.NAME + ".provider");
String evMapStorageProvider = CONFIG.getConfig().get(UserSessionSpi.NAME + ".map.storage.provider");
assumeFalse(MapUserSessionProviderFactory.PROVIDER_ID.equals(evProvider) &&
(evMapStorageProvider == null || FileMapStorageProviderFactory.PROVIDER_ID.equals(evMapStorageProvider)));
// Create user session
String uId = withRealm(this.realmId, (session, realm) -> session.sessions().createUserSession(null, realm, session.users().getUserByUsername(realm, "user1"), "user1", "127.0.0.1", "form", true, null, null, UserSessionModel.SessionPersistenceState.PERSISTENT)).getId();
@ -134,13 +117,5 @@ public class UserSessionConcurrencyTest extends KeycloakModelTest {
});
inComittedTransaction((Consumer<KeycloakSession>) session -> session.realms().removeRealm(realmId));
if (isHotRodStore) {
inComittedTransaction(session -> {
HotRodConnectionProvider provider = session.getProvider(HotRodConnectionProvider.class);
Map<?,?> remoteCache = provider.getRemoteCache(ModelEntityUtil.getModelName(UserSessionModel.class));
assertThat(remoteCache, anEmptyMap());
});
}
}
}

View file

@ -1,74 +0,0 @@
/*
* Copyright 2022 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.model.session;
import org.junit.Test;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
@RequireProvider(value = UserSessionProvider.class, only = MapUserSessionProviderFactory.PROVIDER_ID)
@RequireProvider(RealmProvider.class)
public class UserSessionExpirationTest extends KeycloakModelTest {
private String realmId;
@Override
public void createEnvironment(KeycloakSession s) {
RealmModel realm = createRealm(s, "test");
realm.setDefaultRole(s.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
s.users().addUser(realm, "user1").setEmail("user1@localhost");
this.realmId = realm.getId();
}
@Override
public void cleanEnvironment(KeycloakSession s) {
s.realms().removeRealm(realmId);
}
@Test
public void testSsoSessionIdleTimeout() {
// Set low ssoSessionIdleTimeout
withRealm(realmId, (session, realm) -> {
realm.setSsoSessionIdleTimeout(5);
realm.setSsoSessionMaxLifespan(36000);
realm.setClientSessionIdleTimeout(5);
return null;
});
String uSId= withRealm(realmId, (session, realm) -> session.sessions().createUserSession(null, realm, session.users().getUserByUsername(realm, "user1"), "user1", "127.0.0.1", "form", true, null, null, UserSessionModel.SessionPersistenceState.PERSISTENT).getId());
assertThat(withRealm(realmId, (session, realm) -> session.sessions().getUserSession(realm, uSId)), notNullValue());
setTimeOffset(5);
assertThat(withRealm(realmId, (session, realm) -> session.sessions().getUserSession(realm, uSId)), nullValue());
}
}

View file

@ -33,7 +33,6 @@ import org.keycloak.models.UserProvider;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProviderFactory;
import java.util.LinkedList;
import java.util.List;
@ -155,7 +154,6 @@ public class UserSessionInitializerTest extends KeycloakModelTest {
}
@Test
@RequireProvider(value = UserSessionProvider.class, only = InfinispanUserSessionProviderFactory.PROVIDER_ID)
public void testUserSessionPropagationBetweenSites() throws InterruptedException {
AtomicInteger index = new AtomicInteger();
AtomicReference<String> userSessionId = new AtomicReference<>();

View file

@ -53,7 +53,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
import org.keycloak.models.Constants;
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProviderFactory;
import org.hamcrest.Matchers;
import org.keycloak.storage.client.ClientStorageProvider;
import org.keycloak.storage.client.ClientStorageProviderModel;
@ -67,7 +66,7 @@ import java.util.LinkedList;
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/
@RequireProvider(UserSessionPersisterProvider.class)
@RequireProvider(value = UserSessionProvider.class, only = InfinispanUserSessionProviderFactory.PROVIDER_ID)
@RequireProvider(UserSessionProvider.class)
@RequireProvider(UserProvider.class)
@RequireProvider(RealmProvider.class)
public class UserSessionPersisterProviderTest extends KeycloakModelTest {

View file

@ -17,7 +17,6 @@
package org.keycloak.testsuite.model.session;
import org.hamcrest.Matchers;
import org.infinispan.client.hotrod.RemoteCache;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.models.AuthenticatedClientSessionModel;
@ -26,19 +25,10 @@ import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.UserManager;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.map.storage.ModelEntityUtil;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory;
import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionProvider;
import org.keycloak.models.map.storage.hotRod.userSession.HotRodUserSessionEntity;
import org.keycloak.models.map.userSession.MapUserSessionProvider;
import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProviderFactory;
import org.keycloak.models.sessions.infinispan.changes.sessions.PersisterLastSessionRefreshStoreFactory;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ResetTimeOffsetEvent;
@ -50,7 +40,6 @@ import org.keycloak.timer.TimerProvider;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
@ -59,7 +48,6 @@ import java.util.stream.Collectors;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeFalse;
import static org.keycloak.testsuite.model.session.UserSessionPersisterProviderTest.createClients;
import static org.keycloak.testsuite.model.session.UserSessionPersisterProviderTest.createSessions;
@ -164,22 +152,8 @@ public class UserSessionProviderModelTest extends KeycloakModelTest {
Assert.assertEquals(origSessions[1], userSession);
});
// not possible to expire client session without expiring user sessions with time offset in map storage because
// expiration in map storage takes min of (clientSessionIdleExpiration, ssoSessionIdleTimeout)
inComittedTransaction(session -> {
if (session.getProvider(UserSessionProvider.class) instanceof MapUserSessionProvider) {
RealmModel realm = session.realms().getRealm(realmId);
UserSessionModel userSession = session.sessions().getUserSession(realm, origSessions[0].getId());
userSession.getAuthenticatedClientSessions().values().stream().forEach(clientSession -> {
// expire client sessions
clientSession.setTimestamp(1);
});
} else {
setTimeOffset(1000);
}
});
inComittedTransaction(session -> {
@ -230,7 +204,6 @@ public class UserSessionProviderModelTest extends KeycloakModelTest {
}
@Test
@RequireProvider(value = UserSessionProvider.class, only = InfinispanUserSessionProviderFactory.PROVIDER_ID)
public void testClientSessionIsNotPersistedForTransientUserSession() {
Object[] transientUserSessionWithClientSessionId = inComittedTransaction(session -> {
RealmModel realm = session.realms().getRealm(realmId);
@ -255,33 +228,8 @@ public class UserSessionProviderModelTest extends KeycloakModelTest {
});
}
@Test
@RequireProvider(value = HotRodConnectionProvider.class, only = DefaultHotRodConnectionProviderFactory.PROVIDER_ID)
public void testRemoteCachesParallel() throws InterruptedException {
inIndependentFactories(4, 30, () -> inComittedTransaction(session -> {
HotRodConnectionProvider provider = session.getProvider(HotRodConnectionProvider.class);
RemoteCache<String, HotRodUserSessionEntity> remoteCache = provider.getRemoteCache(ModelEntityUtil.getModelName(UserSessionModel.class));
HotRodUserSessionEntity userSessionEntity = new HotRodUserSessionEntity();
userSessionEntity.id = UUID.randomUUID().toString();
userSessionEntity.realmId = realmId;
remoteCache.put(userSessionEntity.id, userSessionEntity);
}));
inComittedTransaction(session -> {
HotRodConnectionProvider provider = session.getProvider(HotRodConnectionProvider.class);
RemoteCache<String, HotRodUserSessionEntity> remoteCache = provider.getRemoteCache(ModelEntityUtil.getModelName(UserSessionModel.class));
assertThat(remoteCache.size(), Matchers.is(4));
});
}
@Test
public void testCreateUserSessionsParallel() throws InterruptedException {
// Skip the test if MapUserSessionProvider == CHM
String usProvider = CONFIG.getConfig().get("userSessions.provider");
String usMapStorageProvider = CONFIG.getConfig().get("userSessions.map.storage.provider");
assumeFalse(MapUserSessionProviderFactory.PROVIDER_ID.equals(usProvider) &&
(usMapStorageProvider == null || ConcurrentHashMapStorageProviderFactory.PROVIDER_ID.equals(usMapStorageProvider)));
Set<String> userSessionIds = Collections.newSetFromMap(new ConcurrentHashMap<>());
CountDownLatch latch = new CountDownLatch(4);

View file

@ -68,7 +68,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/
@RequireProvider(UserSessionPersisterProvider.class)
@RequireProvider(value=UserSessionProvider.class, only={"infinispan"})
@RequireProvider(UserSessionProvider.class)
@RequireProvider(UserProvider.class)
@RequireProvider(RealmProvider.class)
public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {

View file

@ -26,12 +26,7 @@ import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.SingleUseObjectProvider;
import org.keycloak.models.SingleUseObjectProviderFactory;
import org.keycloak.models.SingleUseObjectSpi;
import org.keycloak.models.UserModel;
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
import org.keycloak.models.map.userSession.MapUserSessionProviderFactory;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
@ -43,7 +38,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeFalse;
@RequireProvider(SingleUseObjectProvider.class)
public class SingleUseObjectModelTest extends KeycloakModelTest {
@ -171,12 +165,6 @@ public class SingleUseObjectModelTest extends KeycloakModelTest {
@Test
public void testCluster() throws InterruptedException {
// Skip the test if SingleUseObjectProvider == CHM
String suProvider = CONFIG.getConfig().get(SingleUseObjectSpi.NAME + ".provider");
String suMapStorageProvider = CONFIG.getConfig().get(SingleUseObjectSpi.NAME + ".map.storage.provider");
assumeFalse(MapSingleUseObjectProviderFactory.PROVIDER_ID.equals(suProvider) &&
(suMapStorageProvider == null || ConcurrentHashMapStorageProviderFactory.PROVIDER_ID.equals(suMapStorageProvider)));
AtomicInteger index = new AtomicInteger();
CountDownLatch afterFirstNodeLatch = new CountDownLatch(1);
CountDownLatch afterDeleteLatch = new CountDownLatch(1);

View file

@ -1,167 +0,0 @@
/*
* Copyright 2021 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.model.storage.tree.sample;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.keycloak.models.map.client.MapClientEntity;
import org.keycloak.models.map.client.MapClientEntityFields;
import org.keycloak.models.map.common.DeepCloner;
import org.keycloak.models.map.common.EntityField;
import org.keycloak.models.map.common.UpdatableEntity;
import org.keycloak.models.map.common.delegate.EntityFieldDelegate;
import org.keycloak.models.map.common.delegate.HasEntityFieldDelegate;
/**
*
* @author hmlnarik
*/
public class Dict<E> extends UpdatableEntity.Impl implements EntityFieldDelegate<E> {
public static final String CLIENT_FIELD_LOGO = "LOGO";
public static final String CLIENT_FIELD_ENABLED = "ENABLED";
public static final String CLIENT_FIELD_NAME = "NAME";
private static final Set<String> CLIENT_ALLOWED_KEYS = new HashSet<>(Arrays.asList(CLIENT_FIELD_NAME, CLIENT_FIELD_ENABLED, CLIENT_FIELD_LOGO));
public static MapClientEntity clientDelegate() {
// To be replaced by dynamic mapper config
Map<String, String> fieldName2key = new HashMap<>();
fieldName2key.put(MapClientEntityFields.ID.getName(), CLIENT_FIELD_NAME);
fieldName2key.put(MapClientEntityFields.CLIENT_ID.getName(), CLIENT_FIELD_NAME);
fieldName2key.put(MapClientEntityFields.ENABLED.getName(), CLIENT_FIELD_ENABLED);
Map<String, String> attributeName2key = new HashMap<>();
attributeName2key.put("logo", CLIENT_FIELD_LOGO);
Dict<MapClientEntity> dict = new Dict<>(CLIENT_ALLOWED_KEYS, fieldName2key, attributeName2key);
return DeepCloner.DUMB_CLONER.entityFieldDelegate(MapClientEntity.class, dict);
}
@SuppressWarnings("unchecked")
public static <E> Dict<E> asDict(E entity) {
return (entity instanceof HasEntityFieldDelegate && ((HasEntityFieldDelegate<?>) entity).getEntityFieldDelegate() instanceof Dict)
? (Dict<E>) ((HasEntityFieldDelegate<E>) entity).getEntityFieldDelegate()
: null;
}
private final Set<String> allowedKeys;
private final Map<String, Object> contents = new HashMap<>();
private final Map<String, String> fieldName2key;
private final Map<String, String> attributeName2key;
public Dict(Set<String> allowedKeys, Map<String, String> fieldName2key, Map<String, String> attributeName2key) {
this.allowedKeys = allowedKeys;
this.fieldName2key = fieldName2key;
this.attributeName2key = attributeName2key;
}
@Override
public <EF extends Enum<? extends EntityField<E>> & EntityField<E>> Object get(EF field) {
if ("Attributes".equals(field.getName())) {
return attributeName2key.entrySet().stream()
.filter(me -> get(me.getValue()) != null)
.collect(Collectors.toMap(me -> me.getKey(), me -> Collections.singletonList(get(me.getValue()))));
}
String key = fieldName2key.get(field.getName());
if (key != null) {
return get(key);
}
return null;
}
@Override
public <T, EF extends Enum<? extends EntityField<E>> & EntityField<E>> void set(EF field, T value) {
String key = fieldName2key.get(field.getName());
if (key != null) {
put(key, value);
}
}
@Override
public <K, EF extends Enum<? extends EntityField<E>> & EntityField<E>> Object mapGet(EF field, K key) {
if ("Attributes".equals(field.getName()) && attributeName2key.containsKey(key)) {
Object v = get(attributeName2key.get(key));
return v == null ? null : Collections.singletonList(get(attributeName2key.get(key)));
}
return null;
}
@Override
public <K, T, EF extends Enum<? extends EntityField<E>> & EntityField<E>> void mapPut(EF field, K key, T value) {
if ("Attributes".equals(field.getName()) && attributeName2key.containsKey(key) && (value instanceof List)) {
List<?> l = (List<?>) value;
if (l.isEmpty()) {
remove(attributeName2key.get(key));
} else {
put(attributeName2key.get(key), l.get(0));
}
}
}
@Override
public <K, EF extends Enum<? extends EntityField<E>> & EntityField<E>> Object mapRemove(EF field, K key) {
if ("Attributes".equals(field.getName()) && attributeName2key.containsKey(key)) {
Object o = remove(attributeName2key.get(key));
return o == null ? null : Collections.singletonList(o);
}
return null;
}
@Override
public <T, EF extends java.lang.Enum<? extends org.keycloak.models.map.common.EntityField<E>> & org.keycloak.models.map.common.EntityField<E>> void collectionAdd(EF field, T value) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public <T, EF extends java.lang.Enum<? extends org.keycloak.models.map.common.EntityField<E>> & org.keycloak.models.map.common.EntityField<E>> Object collectionRemove(EF field, T value) {
throw new UnsupportedOperationException("Not supported yet.");
}
protected boolean isKeyAllowed(String key) {
return allowedKeys.contains(key);
}
public Object get(String key) {
return isKeyAllowed(key) ? contents.get(key) : null;
}
public void put(String key, Object value) {
if (isKeyAllowed(key)) {
updated |= ! Objects.equals(contents.put(key, value), value);
}
}
public Object remove(String key) {
key = key == null ? null : key.toUpperCase();
if (isKeyAllowed(key)) {
Object res = contents.remove(key);
updated |= res != null;
return res;
}
return null;
}
}

View file

@ -1,81 +0,0 @@
/*
* Copyright 2021 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.model.storage.tree.sample;
import org.keycloak.models.map.common.AbstractEntity;
import org.keycloak.models.map.common.DeepCloner;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.QueryParameters;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
/**
*
* @author hmlnarik
*/
public class DictStorage<V extends AbstractEntity, M> implements MapStorage<V, M> {
private final DeepCloner cloner;
private final List<V> store;
public DictStorage(DeepCloner cloner, List<V> store) {
this.cloner = cloner;
this.store = store;
}
List<V> getStore() {
return store;
}
@Override
public V create(V value) {
V res = cloner.from(value);
store.add(res);
return res;
}
@Override
public V read(String key) {
return store.stream()
.filter(e -> Objects.equals(e.getId(), key))
.findFirst()
.orElse(null);
}
@Override
public Stream<V> read(QueryParameters<M> queryParameters) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public long getCount(QueryParameters<M> queryParameters) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public boolean delete(String key) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public long delete(QueryParameters<M> queryParameters) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}

View file

@ -1,60 +0,0 @@
package org.keycloak.testsuite.model.storage.tree.sample;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import java.util.Arrays;
import org.junit.Test;
import org.keycloak.models.map.client.MapClientEntity;
public class DictTest {
@Test
public void testDictClientFromMap() {
MapClientEntity mce = Dict.clientDelegate();
assertThat(mce.getClientId(), nullValue());
assertThat(mce.isEnabled(), nullValue());
assertThat(mce.getAttribute("logo"), nullValue());
assertThat(mce.getAttributes().keySet(), is(empty()));
Dict.asDict(mce).put(Dict.CLIENT_FIELD_NAME, "name");
Dict.asDict(mce).put(Dict.CLIENT_FIELD_ENABLED, false);
Dict.asDict(mce).put(Dict.CLIENT_FIELD_LOGO, "thisShouldBeBase64Logo");
Dict.asDict(mce).put("nonexistent", "nonexistent");
assertThat(mce.getId(), is("name"));
assertThat(mce.getClientId(), is("name"));
assertThat(mce.isEnabled(), is(false));
assertThat(mce.getAttribute("logo"), hasItems("thisShouldBeBase64Logo"));
assertThat(mce.getAttributes().keySet(), hasItems("logo"));
}
@Test
public void testDictClientFromEntity() {
MapClientEntity mce = Dict.clientDelegate();
assertThat(Dict.asDict(mce).get(Dict.CLIENT_FIELD_NAME), nullValue());
assertThat(Dict.asDict(mce).get(Dict.CLIENT_FIELD_ENABLED), nullValue());
assertThat(Dict.asDict(mce).get(Dict.CLIENT_FIELD_LOGO), nullValue());
mce.setClientId("name");
mce.setEnabled(false);
mce.setAttribute("logo", Arrays.asList("thisShouldBeBase64Logo"));
mce.setAttribute("blah", Arrays.asList("thisShouldBeBase64Logofdas"));
assertThat(mce.getAttributes().keySet(), hasItems("logo"));
assertThat(Dict.asDict(mce).get(Dict.CLIENT_FIELD_NAME), is("name"));
assertThat(Dict.asDict(mce).get(Dict.CLIENT_FIELD_ENABLED), is(false));
assertThat(Dict.asDict(mce).get(Dict.CLIENT_FIELD_LOGO), is("thisShouldBeBase64Logo"));
mce.setAttribute("logo", Arrays.asList("thisShouldBeAnotherBase64Logo"));
assertThat(Dict.asDict(mce).get(Dict.CLIENT_FIELD_LOGO), is("thisShouldBeAnotherBase64Logo"));
mce.removeAttribute("logo");
assertThat(Dict.asDict(mce).get(Dict.CLIENT_FIELD_LOGO), nullValue());
}
}

View file

@ -20,43 +20,18 @@ package org.keycloak.testsuite.model.transaction;
import org.junit.Test;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.locking.LockAcquiringTimeoutException;
import org.keycloak.models.map.storage.MapStorageProvider;
import org.keycloak.models.map.storage.hotRod.HotRodMapStorageProviderFactory;
import org.keycloak.models.map.storage.jpa.JpaMapStorageProviderFactory;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
import org.keycloak.testsuite.model.util.TransactionController;
import org.keycloak.utils.LockObjectsForModification;
import jakarta.persistence.OptimisticLockException;
import jakarta.persistence.PessimisticLockException;
import jakarta.transaction.RollbackException;
import java.util.UUID;
import java.util.function.Function;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.internal.matchers.ThrowableCauseMatcher.hasCause;
import static org.keycloak.testsuite.model.util.KeycloakAssertions.assertException;
@RequireProvider(RealmProvider.class)
public class StorageTransactionTest extends KeycloakModelTest {
// System variable is used to simplify configuration for more storages that support pessimistic locking.
// Instead of searching which storage is used and then configure its factory, we can just configure
// lockTimeout like this: .config("lockTimeout", "${keycloak.model.tests.lockTimeout:}") and
// system property will be picked when factory is reinitialized.
public static final String LOCK_TIMEOUT_SYSTEM_PROPERTY = "keycloak.model.tests.lockTimeout";
private String realmId;
@Override
@ -135,235 +110,4 @@ public class StorageTransactionTest extends KeycloakModelTest {
tx3.commit();
}
}
@Test
// LockObjectForModification currently works only in map-jpa and map-hotrod
@RequireProvider(value = MapStorageProvider.class, only = {JpaMapStorageProviderFactory.PROVIDER_ID, HotRodMapStorageProviderFactory.PROVIDER_ID})
public void testLockObjectForModificationById() throws Exception {
testLockObjectForModification(session -> LockObjectsForModification.lockRealmsForModification(session, () -> session.realms().getRealm(realmId)));
}
@Test
// LockObjectForModification currently works only in map-jpa and map-hotrod
@RequireProvider(value = MapStorageProvider.class, only = {JpaMapStorageProviderFactory.PROVIDER_ID, HotRodMapStorageProviderFactory.PROVIDER_ID})
public void testLockUserSessionForModificationByQuery() throws Exception {
// Create user session
final String sessionId = withRealm(realmId, (session, realm) -> {
UserModel myUser = session.users().addUser(realm, "myUser");
return session.sessions().createUserSession(realm, myUser, "myUser", "127.0.0.1", "form", true, null, null).getId();
});
testLockObjectForModification(session -> LockObjectsForModification.lockUserSessionsForModification(session, readUserSessionByIdUsingQueryParameters(session, sessionId)));
}
private <R> void testLockObjectForModification(Function<KeycloakSession, R> lockedExecution) throws Exception {
String originalTimeoutValue = System.getProperty(LOCK_TIMEOUT_SYSTEM_PROPERTY);
try {
System.setProperty(LOCK_TIMEOUT_SYSTEM_PROPERTY, "300");
reinitializeKeycloakSessionFactory();
try (TransactionController tx1 = new TransactionController(getFactory());
TransactionController tx2 = new TransactionController(getFactory());
TransactionController tx3 = new TransactionController(getFactory())) {
tx1.begin();
tx2.begin();
// tx1 acquires lock
tx1.runStep(lockedExecution);
// tx2 should fail as tx1 locked the realm
assertException(() -> tx2.runStep(lockedExecution),
anyOf(allOf(instanceOf(ModelException.class), hasCause(anyOf(instanceOf(PessimisticLockException.class), instanceOf(org.hibernate.PessimisticLockException.class)))),
instanceOf(LockAcquiringTimeoutException.class)));
// end both transactions
tx2.rollback();
tx1.commit();
// start new transaction and read again, it should be successful
tx3.begin();
tx3.runStep(lockedExecution);
tx3.commit();
}
} finally {
if (originalTimeoutValue == null) {
System.clearProperty(LOCK_TIMEOUT_SYSTEM_PROPERTY);
} else {
System.setProperty(LOCK_TIMEOUT_SYSTEM_PROPERTY, originalTimeoutValue);
}
reinitializeKeycloakSessionFactory();
}
}
private LockObjectsForModification.CallableWithoutThrowingAnException<UserSessionModel> readUserSessionByIdUsingQueryParameters(KeycloakSession session, String sessionId) {
RealmModel realm = session.realms().getRealm(realmId);
return () -> session.sessions().getUserSession(realm, sessionId);
}
@Test
// Optimistic locking works only with map-jpa and map-hotrod
@RequireProvider(value = MapStorageProvider.class, only = {JpaMapStorageProviderFactory.PROVIDER_ID,
HotRodMapStorageProviderFactory.PROVIDER_ID})
public void testOptimisticLockingExceptionReadById() throws Exception {
withRealm(realmId, (session, realm) -> {
realm.setDisplayName("displayName1");
return null;
});
try (TransactionController tx1 = new TransactionController(getFactory());
TransactionController tx2 = new TransactionController(getFactory())) {
// tx1 acquires lock
tx1.begin();
tx2.begin();
// both transactions touch the same entity
tx1.runStep(session -> {
session.realms().getRealm(realmId).setDisplayName("displayName2");
return null;
});
tx2.runStep(session -> {
session.realms().getRealm(realmId).setDisplayName("displayName3");
return null;
});
// tx1 transaction should be successful
tx1.commit();
// tx2 should fail as tx1 already changed the value
assertException(tx2::commit,
anyOf(
allOf(instanceOf(RuntimeException.class), hasCause(instanceOf(RollbackException.class))),
allOf(instanceOf(ModelException.class), hasCause(instanceOf(OptimisticLockException.class))),
allOf(instanceOf(OptimisticLockException.class))
));
}
}
@Test
// Optimistic locking works only with map-jpa and map-hotrod
@RequireProvider(value = MapStorageProvider.class, only = {JpaMapStorageProviderFactory.PROVIDER_ID,
HotRodMapStorageProviderFactory.PROVIDER_ID})
public void testOptimisticLockingExceptionReadByQuery() throws Exception {
withRealm(realmId, (session, realm) -> {
realm.setDisplayName("displayName1");
return null;
});
try (TransactionController tx1 = new TransactionController(getFactory());
TransactionController tx2 = new TransactionController(getFactory())) {
// tx1 acquires lock
tx1.begin();
tx2.begin();
// both transactions touch the same entity
tx1.runStep(session -> {
session.realms().getRealmByName("1").setDisplayName("displayName2");
return null;
});
tx2.runStep(session -> {
session.realms().getRealmByName("1").setDisplayName("displayName3");
return null;
});
// tx1 transaction should be successful
tx1.commit();
// tx2 should fail as tx1 already changed the value
assertException(tx2::commit,
anyOf(
allOf(instanceOf(RuntimeException.class), hasCause(instanceOf(RollbackException.class))),
allOf(instanceOf(ModelException.class), hasCause(instanceOf(OptimisticLockException.class))),
allOf(instanceOf(OptimisticLockException.class))
));
}
}
@Test
// Optimistic locking works only with map-jpa and map-hotrod
@RequireProvider(value = MapStorageProvider.class, only = {JpaMapStorageProviderFactory.PROVIDER_ID,
HotRodMapStorageProviderFactory.PROVIDER_ID})
public void testOptimisticLockingDeleteWhenReadingByQuery() throws Exception {
withRealm(realmId, (session, realm) -> {
session.users().addUser(realm, "user", "user", false, false);
return null;
});
try (TransactionController tx1 = new TransactionController(getFactory());
TransactionController tx2 = new TransactionController(getFactory())) {
tx1.begin();
tx2.begin();
// both transactions touch the same entity
tx1.runStep(session -> {
// read by criteria
session.users().getUserByUsername(session.realms().getRealm(realmId), "user").setFirstName("firstName");
return null;
});
tx2.runStep(session -> {
RealmModel realm = session.realms().getRealm(realmId);
// remove by id
session.users().removeUser(realm, session.users().getUserByUsername(realm, "user"));
return null;
});
// tx1 transaction should be successful
tx1.commit();
// tx2 should fail as tx1 already changed the value
assertException(tx2::commit,
anyOf(
allOf(instanceOf(RuntimeException.class), hasCause(instanceOf(RollbackException.class))),
allOf(instanceOf(ModelException.class), hasCause(instanceOf(OptimisticLockException.class))),
allOf(instanceOf(OptimisticLockException.class))
));
}
}
@Test
// Optimistic locking works only with map-jpa and map-hotrod
@RequireProvider(value = MapStorageProvider.class, only = {JpaMapStorageProviderFactory.PROVIDER_ID,
HotRodMapStorageProviderFactory.PROVIDER_ID})
public void testOptimisticLockingDeleteWhenReadingById() throws Exception {
String userId = UUID.randomUUID().toString();
withRealm(realmId, (session, realm) -> {
session.users().addUser(realm, userId, "user", false, false);
return null;
});
try (TransactionController tx1 = new TransactionController(getFactory());
TransactionController tx2 = new TransactionController(getFactory())) {
tx1.begin();
tx2.begin();
// both transactions touch the same entity
tx1.runStep(session -> {
// read by id
session.users().getUserById(session.realms().getRealm(realmId), userId).setFirstName("firstName");
return null;
});
tx2.runStep(session -> {
RealmModel realm = session.realms().getRealm(realmId);
// remove by id after read by id
session.users().removeUser(realm, session.users().getUserById(realm, userId));
return null;
});
// tx1 transaction should be successful
tx1.commit();
// tx2 should fail as tx1 already changed the value
assertException(tx2::commit,
anyOf(
allOf(instanceOf(RuntimeException.class), hasCause(instanceOf(RollbackException.class))),
allOf(instanceOf(ModelException.class), hasCause(instanceOf(OptimisticLockException.class))),
allOf(instanceOf(OptimisticLockException.class))
));
}
}
}

View file

@ -16,17 +16,16 @@
*/
package org.keycloak.testsuite.model.user;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.Constants;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.models.map.realm.MapRealmProviderFactory;
import org.keycloak.models.map.user.MapUserProviderFactory;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderFactory;
import org.keycloak.storage.UserStorageProviderModel;
@ -34,29 +33,23 @@ import org.keycloak.storage.UserStorageUtil;
import org.keycloak.storage.user.UserRegistrationProvider;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
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.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.hamcrest.Matchers;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeThat;
/**
@ -73,8 +66,6 @@ public class UserModelTest extends KeycloakModelTest {
private static final int DELETED_USER_COUNT = LAST_DELETED_USER_INDEX - FIRST_DELETED_USER_INDEX;
private String realmId;
private String realm1Id;
private String realm2Id;
private final List<String> groupIds = new ArrayList<>(NUM_GROUPS);
private String userFederationId;
@ -92,8 +83,6 @@ public class UserModelTest extends KeycloakModelTest {
@Override
public void cleanEnvironment(KeycloakSession s) {
s.realms().removeRealm(realmId);
if (realm1Id != null) s.realms().removeRealm(realm1Id);
if (realm2Id != null) s.realms().removeRealm(realm2Id);
}
@Override
@ -126,67 +115,6 @@ public class UserModelTest extends KeycloakModelTest {
return null;
}
@Test
@RequireProvider(value = UserProvider.class, only = {MapUserProviderFactory.PROVIDER_ID})
@RequireProvider(value = RealmProvider.class, only = {MapRealmProviderFactory.PROVIDER_ID})
public void testCaseSensitivityGetUserByUsername() {
realm1Id = inComittedTransaction((Function<KeycloakSession, String>) session -> {
RealmModel realm = session.realms().createRealm("realm1");
realm.setDefaultRole(session.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
realm.setAttribute(Constants.REALM_ATTR_USERNAME_CASE_SENSITIVE, true);
return realm.getId();
});
withRealm(realm1Id, (session, realm) -> {
UserModel user1 = session.users().addUser(realm, "user");
UserModel user2 = session.users().addUser(realm, "USER");
assertThat(user1, not(nullValue()));
assertThat(user2, not(nullValue()));
assertThat(user1.getUsername(), equalTo("user"));
assertThat(user2.getUsername(), equalTo("USER"));
return null;
});
// try to query storage in a separate transaction to make sure that storage can handle case-sensitive usernames
withRealm(realm1Id, (session, realm) -> {
UserModel user1 = session.users().getUserByUsername(realm, "user");
UserModel user2 = session.users().getUserByUsername(realm, "USER");
assertThat(user1, not(nullValue()));
assertThat(user2, not(nullValue()));
assertThat(user1.getUsername(), equalTo("user"));
assertThat(user2.getUsername(), equalTo("USER"));
return null;
});
realm2Id = inComittedTransaction((Function<KeycloakSession, String>) session -> {
RealmModel realm = session.realms().createRealm("realm2");
realm.setDefaultRole(session.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
realm.setAttribute(Constants.REALM_ATTR_USERNAME_CASE_SENSITIVE, false);
return realm.getId();
});
withRealm(realm2Id, (session, realm) -> {
UserModel user1 = session.users().addUser(realm, "user");
assertThat(user1, not(nullValue()));
try {
session.users().addUser(realm, "USER");
} catch (ModelDuplicateException e) {
return null; // expected
}
fail("ModelDuplicateException expected");
return null;
});
}
@Test
public void testAddRemoveUser() {
inRolledBackTransaction(1, this::addRemoveUser);

View file

@ -1,55 +0,0 @@
/*
* Copyright 2023 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.model.util;
import org.hamcrest.Matcher;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
public class KeycloakAssertions {
/**
* Runs {@code task} and checks whether the execution resulted in
* an exception that matches {@code matcher}. The method fails also
* when no exception is thrown.
*
* @param task task
* @param matcher matcher that the exception should match
*/
public static void assertException(Runnable task, Matcher<? super Throwable> matcher) {
Throwable ex = catchException(task);
assertThat(ex, allOf(notNullValue(), matcher));
}
/**
* Runs the {@code task} and returns any throwable that is thrown.
* If not exception is thrown, the method returns {@code null}
*
* @param task task
*/
public static Throwable catchException(Runnable task) {
try {
task.run();
return null;
} catch (Throwable ex) {
return ex;
}
}
}

View file

@ -42,9 +42,6 @@ log4j.logger.org.keycloak.connections.jpa.updater.liquibase=${keycloak.liquibase
log4j.logger.org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory=debug
# Enable to log short stack traces for log entries enabled with StackUtil.getShortStackTrace() calls
#log4j.logger.org.keycloak.models.map=trace
#log4j.logger.org.keycloak.models.map.transaction=debug
#
#log4j.logger.org.keycloak.STACK_TRACE=trace
#log4j.logger.org.keycloak.models.sessions.infinispan=trace