Simplify configuration for MULTI_SITE

Closes #31807

Signed-off-by: Michal Hajas <mhajas@redhat.com>
This commit is contained in:
Michal Hajas 2024-08-06 18:14:33 +02:00 committed by GitHub
parent 3fbe26d2e1
commit 50c07c6e7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 151 additions and 96 deletions

View file

@ -103,6 +103,7 @@ public class Profile {
TRANSIENT_USERS("Transient users for brokering", Type.EXPERIMENTAL),
MULTI_SITE("Multi-site support", Type.DISABLED_BY_DEFAULT),
REMOTE_CACHE("Remote caches support. Requires Multi-site support to be enabled as well.", Type.EXPERIMENTAL),
CLIENT_TYPES("Client Types", Type.EXPERIMENTAL),

View file

@ -0,0 +1,34 @@
/*
* Copyright 2024 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.common.util;
import org.keycloak.common.Profile;
public class MultiSiteUtils {
public static boolean isMultiSiteEnabled() {
return Profile.isFeatureEnabled(Profile.Feature.MULTI_SITE);
}
/**
* @return true when user sessions are stored in the database. In multi-site setup this is false when REMOTE_CACHE feature is enabled
*/
public static boolean isPersistentSessionsEnabled() {
return Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS) || (isMultiSiteEnabled() && !Profile.isFeatureEnabled(Profile.Feature.REMOTE_CACHE));
}
}

View file

@ -25,6 +25,7 @@ import org.infinispan.persistence.manager.PersistenceManager;
import org.infinispan.util.concurrent.ActionSequencer;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.health.LoadBalancerCheckProvider;
import org.keycloak.health.LoadBalancerCheckProviderFactory;
@ -62,7 +63,7 @@ public class RemoteLoadBalancerCheckProviderFactory implements LoadBalancerCheck
@Override
public boolean isSupported(Config.Scope config) {
return InfinispanUtils.isRemoteInfinispan();
return MultiSiteUtils.isMultiSiteEnabled();
}
@Override

View file

@ -18,9 +18,8 @@
package org.keycloak.infinispan.util;
import org.keycloak.common.Profile;
import org.keycloak.common.Profile.Feature;
import org.keycloak.common.util.MultiSiteUtils;
import static org.keycloak.common.Profile.Feature.MULTI_SITE;
import static org.keycloak.common.Profile.Feature.REMOTE_CACHE;
public final class InfinispanUtils {
@ -39,11 +38,11 @@ public final class InfinispanUtils {
// true if running with external infinispan mode only
public static boolean isRemoteInfinispan() {
return Profile.isFeatureEnabled(Feature.MULTI_SITE) && Profile.isFeatureEnabled(REMOTE_CACHE);
return MultiSiteUtils.isMultiSiteEnabled() || Profile.isFeatureEnabled(REMOTE_CACHE);
}
// true if running with embedded caches.
public static boolean isEmbeddedInfinispan() {
return !Profile.isFeatureEnabled(MULTI_SITE) || !Profile.isFeatureEnabled(REMOTE_CACHE);
return !isRemoteInfinispan();
}
}

View file

@ -32,8 +32,8 @@ import org.infinispan.persistence.remote.RemoteStore;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.cluster.ClusterProvider;
import org.keycloak.common.Profile;
import org.keycloak.common.util.Environment;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.common.util.Time;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.connections.infinispan.InfinispanUtil;
@ -77,8 +77,6 @@ import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderEventListener;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
import static org.keycloak.common.Profile.Feature.PERSISTENT_USER_SESSIONS;
public class InfinispanUserSessionProviderFactory implements UserSessionProviderFactory<UserSessionProvider>, ServerInfoAwareProviderFactory, EnvironmentDependentProviderFactory {
private static final Logger log = Logger.getLogger(InfinispanUserSessionProviderFactory.class);
@ -131,7 +129,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
offlineClientSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME);
}
if (Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (MultiSiteUtils.isPersistentSessionsEnabled()) {
return new PersistentUserSessionProvider(
session,
remoteCacheInvoker,
@ -175,8 +173,9 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
offlineSessionCacheEntryLifespanOverride = config.getInt(CONFIG_OFFLINE_SESSION_CACHE_ENTRY_LIFESPAN_OVERRIDE, -1);
offlineClientSessionCacheEntryLifespanOverride = config.getInt(CONFIG_OFFLINE_CLIENT_SESSION_CACHE_ENTRY_LIFESPAN_OVERRIDE, -1);
maxBatchSize = config.getInt(CONFIG_MAX_BATCH_SIZE, DEFAULT_MAX_BATCH_SIZE);
// Do not use caches for sessions if explicitly disabled or if embedded caches are not used
useCaches = config.getBoolean(CONFIG_USE_CACHES, DEFAULT_USE_CACHES) && InfinispanUtils.isEmbeddedInfinispan();
useBatches = config.getBoolean(CONFIG_USE_BATCHES, DEFAULT_USE_BATCHES) && Profile.isFeatureEnabled(PERSISTENT_USER_SESSIONS);
useBatches = config.getBoolean(CONFIG_USE_BATCHES, DEFAULT_USE_BATCHES) && MultiSiteUtils.isPersistentSessionsEnabled();
if (useBatches) {
asyncQueuePersistentUpdate = new ArrayBlockingQueue<>(1000);
}
@ -204,7 +203,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
keyGenerator = new InfinispanKeyGenerator();
checkRemoteCaches(session);
if (!Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (!MultiSiteUtils.isPersistentSessionsEnabled()) {
initializeLastSessionRefreshStore(factory);
}
registerClusterListeners(session);
@ -237,7 +236,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
}
}
});
if (Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS) && useBatches) {
if (MultiSiteUtils.isPersistentSessionsEnabled() && useBatches) {
persistentSessionsWorker = new PersistentSessionsWorker(factory,
asyncQueuePersistentUpdate,
maxBatchSize);
@ -367,7 +366,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
remoteCacheInvoker.addRemoteCache(ispnCache.getName(), remoteCache, maxIdleLoader);
Runnable onFailover = null;
if (useCaches && Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (useCaches && MultiSiteUtils.isPersistentSessionsEnabled()) {
// If persistent sessions are enabled, we want to clear the local caches when a failover of the listener on the remote store changes as we might have missed some of the remote store events
// which might have been triggered by another Keycloak site connected to the same remote Infinispan cluster.
// Due to this, we can be sure that we never have outdated information in our local cache. All entries will be re-loaded from the remote cache or the database as necessary lazily.
@ -465,7 +464,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
@Override
public boolean isSupported(Config.Scope config) {
return InfinispanUtils.isEmbeddedInfinispan() || Profile.isFeatureEnabled(PERSISTENT_USER_SESSIONS);
return InfinispanUtils.isEmbeddedInfinispan() || MultiSiteUtils.isPersistentSessionsEnabled();
}
@Override

View file

@ -48,6 +48,7 @@ import org.jboss.logging.Logger;
import org.keycloak.cluster.ClusterProvider;
import org.keycloak.common.Profile;
import org.keycloak.common.Profile.Feature;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.common.util.Retry;
import org.keycloak.common.util.Time;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
@ -134,7 +135,7 @@ public class PersistentUserSessionProvider implements UserSessionProvider, Sessi
SerializeExecutionsByKey<String> serializerOfflineSession,
SerializeExecutionsByKey<UUID> serializerClientSession,
SerializeExecutionsByKey<UUID> serializerOfflineClientSession) {
if (!Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (!MultiSiteUtils.isPersistentSessionsEnabled()) {
throw new IllegalStateException("Persistent user sessions are not enabled");
}

View file

@ -17,7 +17,7 @@
package org.keycloak.models.sessions.infinispan;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
@ -231,7 +231,7 @@ public class UserSessionAdapter<T extends SessionRefreshStore & UserSessionProvi
return;
}
if (!Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS) && offline) {
if (!MultiSiteUtils.isPersistentSessionsEnabled() && offline) {
// Received the message from the other DC that we should update the lastSessionRefresh in local cluster. Don't update DB in that case.
// The other DC already did.
Boolean ignoreRemoteCacheUpdate = (Boolean) session.getAttribute(CrossDCLastSessionRefreshListener.IGNORE_REMOTE_CACHE_UPDATE);

View file

@ -19,7 +19,7 @@ package org.keycloak.models.sessions.infinispan.entities;
import org.infinispan.api.annotations.indexing.Basic;
import org.infinispan.protostream.annotations.ProtoField;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
/**
@ -71,14 +71,14 @@ public abstract class SessionEntity {
public abstract int hashCode();
public boolean isOffline() {
if (!Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (!MultiSiteUtils.isPersistentSessionsEnabled()) {
throw new IllegalArgumentException("Offline flags are not supported in non-persistent-session environments.");
}
return isOffline;
}
public void setOffline(boolean offline) {
if (!Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (!MultiSiteUtils.isPersistentSessionsEnabled()) {
throw new IllegalArgumentException("Offline flags are not supported in non-persistent-session environments.");
}
isOffline = offline;

View file

@ -5,7 +5,7 @@ import java.util.UUID;
import org.infinispan.client.hotrod.RemoteCache;
import org.keycloak.Config;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.infinispan.util.InfinispanUtils;
import org.keycloak.models.KeycloakSession;
@ -69,7 +69,7 @@ public class RemoteUserSessionProviderFactory implements UserSessionProviderFact
@Override
public boolean isSupported(Config.Scope config) {
return InfinispanUtils.isRemoteInfinispan() && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS);
return InfinispanUtils.isRemoteInfinispan() && !MultiSiteUtils.isPersistentSessionsEnabled();
}
@Override

View file

@ -18,7 +18,7 @@
package org.keycloak.models.sessions.infinispan.remotestore;
import org.infinispan.client.hotrod.exceptions.HotRodClientException;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.common.util.Retry;
import java.util.Collections;
import java.util.HashMap;
@ -152,7 +152,7 @@ public class RemoteCacheInvoker {
VersionedValue<SessionEntityWrapper<V>> versioned = remoteCache.getWithMetadata(key);
if (versioned == null) {
if (Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS) &&
if (MultiSiteUtils.isPersistentSessionsEnabled() &&
(remoteCache.getName().equals(USER_SESSION_CACHE_NAME)
|| remoteCache.getName().equals(CLIENT_SESSION_CACHE_NAME)
|| remoteCache.getName().equals(OFFLINE_USER_SESSION_CACHE_NAME)

View file

@ -18,7 +18,7 @@
package org.keycloak.models.jpa.session;
import org.jboss.logging.Logger;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.common.util.Time;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
@ -253,7 +253,7 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
expire(realm, expiredClientOffline, expiredOffline, true);
if (Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (MultiSiteUtils.isPersistentSessionsEnabled()) {
int expired = Time.currentTime() - Math.max(realm.getSsoSessionIdleTimeout(), realm.getSsoSessionIdleTimeoutRememberMe()) - SessionTimeoutHelper.PERIODIC_CLEANER_IDLE_TIMEOUT_WINDOW_SECONDS;

View file

@ -18,7 +18,7 @@
package org.keycloak.models.session;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelException;
@ -216,7 +216,7 @@ public class PersistentUserSessionAdapter implements OfflineUserSessionModel {
@Override
public String getLoginUsername() {
if (isOffline() || !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (isOffline() || !MultiSiteUtils.isPersistentSessionsEnabled()) {
return getUser().getUsername();
} else {
return getData().getLoginUsername();

View file

@ -68,6 +68,7 @@ import org.keycloak.authorization.policy.provider.PolicySpi;
import org.keycloak.authorization.policy.provider.js.DeployedScriptPolicyFactory;
import org.keycloak.common.Profile;
import org.keycloak.common.crypto.FipsMode;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.common.util.StreamUtil;
import org.keycloak.config.DatabaseOptions;
import org.keycloak.config.HealthOptions;
@ -644,7 +645,7 @@ class KeycloakProcessor {
JsResource.class.getName())), false));
}
if (!Profile.isFeatureEnabled(Profile.Feature.MULTI_SITE)) {
if (!MultiSiteUtils.isMultiSiteEnabled()) {
buildTimeConditionBuildItemBuildProducer.produce(new BuildTimeConditionBuildItem(index.getIndex().getClassByName(DotName.createSimple(
LoadBalancerResource.class.getName())), false));
}

View file

@ -58,6 +58,7 @@ import org.jgroups.protocols.UDP;
import org.jgroups.util.TLS;
import org.jgroups.util.TLSClientAuth;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.config.CachingOptions;
import org.keycloak.config.MetricsOptions;
import org.keycloak.infinispan.util.InfinispanUtils;
@ -297,7 +298,7 @@ public class CacheManagerFactory {
Arrays.stream(CLUSTERED_CACHE_NAMES).forEach(cacheName -> {
if (cacheName.equals(USER_SESSION_CACHE_NAME) || cacheName.equals(CLIENT_SESSION_CACHE_NAME) || cacheName.equals(OFFLINE_USER_SESSION_CACHE_NAME) || cacheName.equals(OFFLINE_CLIENT_SESSION_CACHE_NAME)) {
ConfigurationBuilder configurationBuilder = builder.getNamedConfigurationBuilders().get(cacheName);
if (Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (MultiSiteUtils.isPersistentSessionsEnabled()) {
if (configurationBuilder.memory().maxCount() == -1) {
logger.infof("Persistent user sessions enabled and no memory limit found in configuration. Setting max entries for %s to 10000 entries.", cacheName);
configurationBuilder.memory().maxCount(10000);

View file

@ -122,13 +122,4 @@ public class HealthDistTest {
distribution.stop();
}
}
@Test
@Launch({ "start-dev", "--features=multi-site" })
void testLoadBalancerCheck(KeycloakDistribution distribution) {
distribution.setRequestPort(8080);
when().get("/lb-check").then()
.statusCode(200);
}
}

View file

@ -34,8 +34,20 @@ import static io.restassured.RestAssured.when;
public class ExternalInfinispanTest {
@Test
@Launch({ "start-dev", "--features=multi-site", "--cache=ispn", "--cache-config-file=../../../test-classes/ExternalInfinispan/kcb-infinispan-cache-remote-store-config.xml", "--spi-connections-infinispan-quarkus-site-name=ISPN" })
void testLoadBalancerCheckFailure() {
@Launch({
"start-dev",
"--features=multi-site",
"--cache=ispn",
"--cache-remote-host=localhost",
"--cache-remote-username=keycloak",
"--cache-remote-password=Password1!",
"--cache-remote-tls-enabled=false",
"--spi-connections-infinispan-quarkus-site-name=ISPN",
"--spi-load-balancer-check-remote-poll-interval=500",
"-Dkc.cache-remote-create-caches=true",
"--verbose"
})
void testLoadBalancerCheckFailureWithMultiSite() {
runLoadBalancerCheckFailureTest();
}
@ -47,9 +59,10 @@ public class ExternalInfinispanTest {
"--cache-remote-host=localhost",
"--cache-remote-username=keycloak",
"--cache-remote-password=Password1!",
"--cache-remote-tls-enabled=false",
"--spi-connections-infinispan-quarkus-site-name=ISPN",
"--spi-load-balancer-check-remote-poll-interval=500",
"-Dkc.cache-remote-tls-enabled=false",
"-Dkc.cache-remote-create-caches=true",
"--verbose"
})
void testLoadBalancerCheckFailureWithRemoteOnlyCaches() {
@ -71,7 +84,12 @@ public class ExternalInfinispanTest {
}
@Test
@Launch({ "start-dev", "--features=multi-site", "--cache=ispn", "--cache-config-file=../../../test-classes/ExternalInfinispan/kcb-infinispan-cache-remote-store-config.xml", "-Djboss.site.name=ISPN" })
@Launch({
"start-dev",
"--cache=ispn",
"-Djboss.site.name=ISPN",
"--verbose"
})
void testSiteNameAsSystemProperty(LaunchResult result) {
((CLIResult) result).assertMessage("System property jboss.site.name is in use. Use --spi-connections-infinispan-quarkus-site-name config option instead");
}

View file

@ -29,7 +29,7 @@ public interface ManagedCacheManagerProvider {
<C> C getEmbeddedCacheManager(Config.Scope config);
/**
* @return A RemoteCacheManager if the feature {@link org.keycloak.common.Profile.Feature#REMOTE_CACHE} is enabled, {@code null} otherwise.
* @return A RemoteCacheManager if the features {@link org.keycloak.common.Profile.Feature#REMOTE_CACHE} or {@link org.keycloak.common.Profile.Feature#MULTI_SITE} is enabled, {@code null} otherwise.
*/
<C> C getRemoteCacheManager(Config.Scope config);
}

View file

@ -18,6 +18,7 @@
package org.keycloak.services.resteasy;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.services.error.KcUnrecognizedPropertyExceptionHandler;
@ -62,7 +63,7 @@ public class ResteasyKeycloakApplication extends KeycloakApplication {
singletons.add(new ObjectMapperResolver());
classes.add(WelcomeResource.class);
if (Profile.isFeatureEnabled(Profile.Feature.MULTI_SITE)) {
if (MultiSiteUtils.isMultiSiteEnabled()) {
// If we are running in multi-site mode, we need to add a resource which to expose
// an endpoint for the load balancer to gather information whether this site should receive requests or not.
classes.add(LoadBalancerResource.class);

View file

@ -212,7 +212,7 @@ public abstract class AbstractQuarkusDeployableContainer implements DeployableCo
addFeaturesOption(commands);
var features = getDefaultFeatures();
if (features.contains("remote-cache") && features.contains("multi-site")) {
if (features.contains("remote-cache") || features.contains("multi-site")) {
commands.add("--cache-remote-host=127.0.0.1");
commands.add("--cache-remote-username=keycloak");
commands.add("--cache-remote-password=Password1!");

View file

@ -25,6 +25,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.keycloak.common.Profile;
import org.keycloak.common.util.Time;
import org.keycloak.infinispan.util.InfinispanUtils;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
@ -214,7 +215,7 @@ public class AuthenticationSessionProviderTest extends AbstractTestRealmKeycloak
@Test
@ModelTest
public void testExpiredAuthSessions(KeycloakSession session) {
assumeFalse(Profile.isFeatureEnabled(Profile.Feature.REMOTE_CACHE));
assumeFalse(InfinispanUtils.isRemoteInfinispan());
AtomicReference<String> authSessionID = new AtomicReference<>();
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), mainSession -> {

View file

@ -389,6 +389,8 @@ public class AccessTokenTest extends AbstractKeycloakTest {
@Test
public void accessTokenCodeExpired() {
ProfileAssume.assumeFeatureDisabled(Profile.Feature.REMOTE_CACHE);
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MULTI_SITE);
getTestingClient().testing().setTestingInfinispanTimeService();
RealmManager.realm(adminClient.realm("test")).accessCodeLifeSpan(1);
oauth.doLogin("test-user@localhost", "password");

View file

@ -66,6 +66,8 @@ public class LastSessionRefreshUnitTest extends AbstractKeycloakTest {
@Test
public void testLastSessionRefreshCounters() {
ProfileAssume.assumeFeatureDisabled(Profile.Feature.REMOTE_CACHE);
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MULTI_SITE);
testingClient.server().run(new LastSessionRefreshServerCounterTest());
}
@ -111,6 +113,8 @@ public class LastSessionRefreshUnitTest extends AbstractKeycloakTest {
@Test
public void testLastSessionRefreshIntervals() {
ProfileAssume.assumeFeatureDisabled(Profile.Feature.REMOTE_CACHE);
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MULTI_SITE);
testingClient.server().run(new LastSessionRefreshServerIntervalsTest());
}

View file

@ -78,6 +78,12 @@
<groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-tests-base</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-quarkus-server</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>${jdbc.mvn.groupId}</groupId>
@ -118,6 +124,10 @@
<artifactId>infinispan-component-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-remote-query-server</artifactId>
</dependency>
</dependencies>
<build>
@ -286,12 +296,6 @@
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-remote-query-server</artifactId>
</dependency>
</dependencies>
</profile>
<profile>

View file

@ -21,6 +21,8 @@ import org.junit.Assume;
import org.junit.Test;
import org.keycloak.common.Profile;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.connections.infinispan.InfinispanUtil;
import org.keycloak.infinispan.util.InfinispanUtils;
import org.keycloak.models.cache.infinispan.events.AuthenticationSessionAuthNoteUpdateEvent;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
@ -56,7 +58,7 @@ public class CacheExpirationTest extends KeycloakModelTest {
@Test
public void testCacheExpiration() throws Exception {
assumeFalse("Embedded caches not available for testing.", Profile.isFeatureEnabled(Profile.Feature.MULTI_SITE) && Profile.isFeatureEnabled(Profile.Feature.REMOTE_CACHE));
assumeFalse("Embedded caches not available for testing.", InfinispanUtils.isRemoteInfinispan());
log.debugf("Number of previous instances of the class on the heap: %d", getNumberOfInstancesOfClass(AuthenticationSessionAuthNoteUpdateEvent.class));

View file

@ -23,6 +23,7 @@ import java.util.function.Predicate;
import org.infinispan.commons.CacheConfigurationException;
import org.junit.Test;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.infinispan.util.InfinispanUtils;
import org.keycloak.testsuite.model.KeycloakModelTest;
@ -60,8 +61,7 @@ public class FeatureEnabledTest extends KeycloakModelTest {
@Test
public void testRemoteCachesOnly() {
assumeTrue("Remote-Cache Feature disabled", Profile.isFeatureEnabled(Profile.Feature.REMOTE_CACHE));
assumeTrue("Multi-Site Feature disabled", Profile.isFeatureEnabled(Profile.Feature.MULTI_SITE));
assumeTrue("Remote-Cache Feature disabled", Profile.isFeatureEnabled(Profile.Feature.REMOTE_CACHE) || MultiSiteUtils.isMultiSiteEnabled());
assertTrue(InfinispanUtils.isRemoteInfinispan());
assertFalse(InfinispanUtils.isEmbeddedInfinispan());
inComittedTransaction(session -> {
@ -72,22 +72,9 @@ public class FeatureEnabledTest extends KeycloakModelTest {
});
}
@Test
public void testRemoteAndEmbeddedCaches() {
assumeTrue("Multi-Site Feature disabled", Profile.isFeatureEnabled(Profile.Feature.MULTI_SITE));
assumeFalse("Remote-Cache Feature enabled", Profile.isFeatureEnabled(Profile.Feature.REMOTE_CACHE));
assertFalse(InfinispanUtils.isRemoteInfinispan());
assertTrue(InfinispanUtils.isEmbeddedInfinispan());
inComittedTransaction(session -> {
var clusterProvider = session.getProvider(InfinispanConnectionProvider.class);
Arrays.stream(CLUSTERED_CACHE_NAMES).forEach(s -> assertEmbeddedCacheExists(clusterProvider, s));
Arrays.stream(CLUSTERED_CACHE_NAMES).forEach(s -> assertRemoteCacheExists(clusterProvider, s));
});
}
@Test
public void testEmbeddedCachesOnly() {
assumeFalse("Multi-Site Feature enabled", Profile.isFeatureEnabled(Profile.Feature.MULTI_SITE));
assumeFalse("Multi-Site Feature enabled", MultiSiteUtils.isMultiSiteEnabled());
assumeFalse("Remote-Cache Feature enabled", Profile.isFeatureEnabled(Profile.Feature.REMOTE_CACHE));
assertFalse(InfinispanUtils.isRemoteInfinispan());
assertTrue(InfinispanUtils.isEmbeddedInfinispan());

View file

@ -25,6 +25,7 @@ import org.keycloak.testsuite.model.HotRodServerRule;
import org.keycloak.testsuite.model.KeycloakModelParameters;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@ -66,7 +67,7 @@ public class CrossDCInfinispan extends KeycloakModelParameters {
}
public CrossDCInfinispan() {
super(Infinispan.ALLOWED_SPIS, Infinispan.ALLOWED_FACTORIES);
super(Infinispan.ALLOWED_SPIS, Stream.concat(Infinispan.ALLOWED_FACTORIES.stream(), RemoteInfinispan.ALLOWED_FACTORIES.stream()).collect(Collectors.toSet()));
}
@Override

View file

@ -37,6 +37,7 @@ import java.util.stream.Stream;
import org.hamcrest.Matchers;
import org.infinispan.commons.CacheException;
import org.junit.Test;
import org.keycloak.infinispan.util.InfinispanUtils;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
@ -50,13 +51,13 @@ import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProvider;
import org.keycloak.models.sessions.infinispan.PersistentUserSessionProvider;
import org.keycloak.models.sessions.infinispan.remote.RemoteUserSessionProvider;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assume.assumeTrue;
/**
*
@ -252,6 +253,8 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
@Test
@RequireProvider(UserSessionPersisterProvider.class)
public void testOfflineSessionLoadingAfterCacheRemoval() {
assumeTrue("Run only if Embedded Infinispan is used for storing/caching sessions.", InfinispanUtils.isEmbeddedInfinispan());
List<String> offlineSessionIds = createOfflineSessions(realmId, userIds);
assertOfflineSessionsExist(realmId, offlineSessionIds);
@ -268,8 +271,6 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
((InfinispanUserSessionProvider) provider).removeLocalUserSessions(realm.getId(), true);
} else if (provider instanceof PersistentUserSessionProvider) {
((PersistentUserSessionProvider) provider).removeLocalUserSessions(realm.getId(), true);
} else if (provider instanceof RemoteUserSessionProvider) {
//no-op, session not local
} else {
throw new IllegalStateException("Unknown UserSessionProvider: " + provider);
}

View file

@ -26,7 +26,7 @@ import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runners.MethodSorters;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.common.util.Retry;
import org.keycloak.common.util.Time;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
@ -298,7 +298,7 @@ public class SessionTimeoutsTest extends KeycloakModelTest {
return null;
});
if (Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (MultiSiteUtils.isPersistentSessionsEnabled()) {
// The persistent session will write the update data asynchronously, wait for it to arrive.
Retry.executeWithBackoff(iteration -> {
withRealm(realmId, (session, realm) -> {

View file

@ -28,6 +28,7 @@ import org.infinispan.Cache;
import org.infinispan.client.hotrod.RemoteCache;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.infinispan.util.InfinispanUtils;
import org.keycloak.models.ClientModel;
@ -48,6 +49,7 @@ import org.keycloak.testsuite.model.RequireProvider;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Every.everyItem;
import static org.hamcrest.core.Is.is;
import static org.junit.Assume.assumeFalse;
import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.USER_SESSION_CACHE_NAME;
/**
@ -157,6 +159,7 @@ public class UserSessionInitializerTest extends KeycloakModelTest {
@Test
public void testUserSessionPropagationBetweenSites() throws InterruptedException {
assumeFalse("Run only if Infinispan caches are used for storing/caching sessions", MultiSiteUtils.isMultiSiteEnabled() && MultiSiteUtils.isPersistentSessionsEnabled());
AtomicInteger index = new AtomicInteger();
AtomicReference<String> userSessionId = new AtomicReference<>();
AtomicReference<List<Boolean>> containsSession = new AtomicReference<>(new LinkedList<>());

View file

@ -21,7 +21,7 @@ import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.common.util.Time;
import org.keycloak.infinispan.util.InfinispanUtils;
import org.keycloak.models.AuthenticatedClientSessionModel;
@ -151,7 +151,7 @@ public class UserSessionPersisterProviderTest extends KeycloakModelTest {
.forEach(userSessionLooper -> persistUserSession(session, userSessionLooper, true));
});
if (!Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (!MultiSiteUtils.isPersistentSessionsEnabled()) {
inComittedTransaction(session -> {
// Persist 1 online session
RealmModel realm = session.realms().getRealm(realmId);
@ -581,7 +581,8 @@ public class UserSessionPersisterProviderTest extends KeycloakModelTest {
@Test
public void testMigrateSession() {
Assume.assumeTrue(Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS));
Assume.assumeTrue(MultiSiteUtils.isPersistentSessionsEnabled());
Assume.assumeTrue(InfinispanUtils.isEmbeddedInfinispan());
UserSessionModel[] sessions = inComittedTransaction(session -> {
// Create some sessions in infinispan

View file

@ -20,6 +20,7 @@ import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
@ -129,7 +130,7 @@ public class UserSessionProviderModelTest extends KeycloakModelTest {
// skip for persistent user sessions as the periodic task is not used there
TimerProvider timer = kcSession.getProvider(TimerProvider.class);
TimerProvider.TimerTaskContext timerTaskCtx = null;
if (timer != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (timer != null && !MultiSiteUtils.isPersistentSessionsEnabled()) {
timerTaskCtx = timer.cancelTask(PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
log.info("Cancelled periodic task " + PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}
@ -181,7 +182,7 @@ public class UserSessionProviderModelTest extends KeycloakModelTest {
setTimeOffset(0);
kcSession.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent());
// Enable periodic task again, skip for persistent user sessions as the periodic task is not used there
if (timer != null && timerTaskCtx != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (timer != null && timerTaskCtx != null && !MultiSiteUtils.isPersistentSessionsEnabled()) {
timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}

View file

@ -41,6 +41,7 @@ import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.keycloak.common.Profile;
import org.keycloak.common.util.MultiSiteUtils;
import org.keycloak.common.util.Time;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.infinispan.util.InfinispanUtils;
@ -126,7 +127,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
// skip for persistent user sessions as the periodic task is not used there
TimerProvider timer = kcSession.getProvider(TimerProvider.class);
TimerProvider.TimerTaskContext timerTaskCtx = null;
if (timer != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (timer != null && !MultiSiteUtils.isPersistentSessionsEnabled()) {
timerTaskCtx = timer.cancelTask(PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
log.info("Cancelled periodic task " + PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}
@ -245,7 +246,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
setTimeOffset(0);
kcSession.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent());
// Enable periodic task again, skip for persistent user sessions as the periodic task is not used there
if (timer != null && timerTaskCtx != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (timer != null && timerTaskCtx != null && !MultiSiteUtils.isPersistentSessionsEnabled()) {
timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}
@ -259,7 +260,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
// skip for persistent user sessions as the periodic task is not used there
TimerProvider timer = kcSession.getProvider(TimerProvider.class);
TimerProvider.TimerTaskContext timerTaskCtx = null;
if (timer != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (timer != null && !MultiSiteUtils.isPersistentSessionsEnabled()) {
timerTaskCtx = timer.cancelTask(PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
log.info("Cancelled periodic task " + PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}
@ -314,14 +315,14 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
session.sessions().createOfflineUserSession(userSession);
session.sessions().createOfflineUserSession(origSessions[0]);
if (!Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS) && InfinispanUtils.isEmbeddedInfinispan()) {
if (!MultiSiteUtils.isPersistentSessionsEnabled() && InfinispanUtils.isEmbeddedInfinispan()) {
// This does not work with persistent user sessions because we currently have two transactions and the one that creates the offline user sessions is not committing the changes
// try to load user session from persister
Assert.assertEquals(2, persister.loadUserSessionsStream(0, 10, true, "00000000-0000-0000-0000-000000000000").count());
}
});
if (Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS) && InfinispanUtils.isEmbeddedInfinispan()) {
if (MultiSiteUtils.isPersistentSessionsEnabled() && InfinispanUtils.isEmbeddedInfinispan()) {
inComittedTransaction(session -> {
persister = session.getProvider(UserSessionPersisterProvider.class);
Assert.assertEquals(2, persister.loadUserSessionsStream(0, 10, true, "00000000-0000-0000-0000-000000000000").count());
@ -333,7 +334,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
kcSession.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent());
// Enable periodic task again, skip for persistent user sessions as the periodic task is not used there
if (timer != null && timerTaskCtx != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
if (timer != null && timerTaskCtx != null && !MultiSiteUtils.isPersistentSessionsEnabled()) {
timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}
@ -502,7 +503,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
// skip the test for CrossDC
Assume.assumeFalse(Objects.equals(CONFIG.scope("connectionsInfinispan.default").get("remoteStoreEnabled"), "true"));
// As offline session's timeout is not overriden when PERSISTENT_USER_SESSIONS is enabled
Assume.assumeFalse(Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS));
Assume.assumeFalse(MultiSiteUtils.isPersistentSessionsEnabled());
createOfflineSessions("user1", 2, new LinkedList<>(), new LinkedList<>());