From 5ba004b447120ed94d92e3a7721662f749749939 Mon Sep 17 00:00:00 2001 From: Martin Kanis Date: Mon, 29 Aug 2022 16:12:28 +0200 Subject: [PATCH] Leverage Infinispan lifespan for ExpirableEntities in HotRod storage --- .../map/storage/hotRod/HotRodMapStorage.java | 53 ++++++--- ...HotRodRootAuthenticationSessionEntity.java | 1 - ...efaultHotRodConnectionProviderFactory.java | 10 +- .../hotRod/events/HotRodAdminEventEntity.java | 1 - .../hotRod/events/HotRodAuthEventEntity.java | 1 - .../userSession/HotRodUserSessionEntity.java | 1 - .../HotRodUserSessionTransaction.java | 6 +- .../services/testsuite-providers/pom.xml | 5 + .../infinispan/InfinispanTimeServiceTask.java | 112 ++++++++++++++++++ .../rest/TestingResourceProvider.java | 20 ++++ .../services/org.infinispan.tasks.ServerTask | 1 + .../integration-arquillian/tests/base/pom.xml | 13 ++ .../arquillian/HotRodStoreTestEnricher.java | 5 +- .../testsuite/util/InfinispanContainer.java | 16 +++ .../testsuite/AbstractKeycloakTest.java | 1 + .../testsuite/admin/TimeOffsetTest.java | 66 +++++++++++ testsuite/model/pom.xml | 20 +++- .../testsuite/model/KeycloakModelTest.java | 30 ++++- .../testsuite/model/MigrationModelTest.java | 5 +- .../testsuite/model/TimeOffsetTest.java | 82 +++++++++++++ .../model/events/EventQueryTest.java | 5 +- .../session/AuthenticationSessionTest.java | 5 +- .../session/UserSessionExpirationTest.java | 4 +- .../UserSessionPersisterProviderTest.java | 7 +- .../session/UserSessionProviderModelTest.java | 5 +- .../UserSessionProviderOfflineModelTest.java | 17 ++- .../SingleUseObjectModelTest.java | 5 +- 27 files changed, 435 insertions(+), 62 deletions(-) create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/model/infinispan/InfinispanTimeServiceTask.java create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.infinispan.tasks.ServerTask create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/TimeOffsetTest.java create mode 100644 testsuite/model/src/test/java/org/keycloak/testsuite/model/TimeOffsetTest.java diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorage.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorage.java index cba513f795..85daded7b3 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorage.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorage.java @@ -45,12 +45,12 @@ import org.keycloak.storage.SearchableModelField; import java.util.Map; import java.util.Objects; import java.util.Spliterators; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import static org.keycloak.models.map.common.ExpirationUtils.isExpired; import static org.keycloak.models.map.storage.hotRod.common.HotRodUtils.paginateQuery; import static org.keycloak.utils.StreamsUtil.closing; @@ -82,6 +82,17 @@ public class HotRodMapStorage 0) { + remoteCache.putIfAbsent(key, value.getHotRodEntity(), lifespan, TimeUnit.MILLISECONDS); + } else { + LOG.warnf("Skipped creation of entity %s in storage due to negative/zero lifespan.", key); + } + return value; + } + } remoteCache.putIfAbsent(key, value.getHotRodEntity()); return value; @@ -97,20 +108,28 @@ public class HotRodMapStorage 0) { + previousValue = remoteCache.replace(key, value.getHotRodEntity(), lifespan, TimeUnit.MILLISECONDS); + } else { + LOG.warnf("Removing entity %s from storage due to negative/zero lifespan.", key); + previousValue = remoteCache.remove(key); + } + return previousValue == null ? null : delegateProducer.apply(previousValue); + } + } E previousValue = remoteCache.replace(key, value.getHotRodEntity()); - if (previousValue == null) return null; - - return delegateProducer.apply(previousValue); + return previousValue == null ? null : delegateProducer.apply(previousValue); } @Override @@ -127,22 +146,12 @@ public class HotRodMapStorage " + Time.currentTimeMillis() + " OR " - + IckleQueryOperators.C + ".expiration is null)"; - } - @Override public Stream read(QueryParameters queryParameters) { IckleQueryMapModelCriteriaBuilder iqmcb = queryParameters.getModelCriteriaBuilder() .flashToModelCriteriaBuilder(createCriteriaBuilder()); String queryString = iqmcb.getIckleQuery(); - // Temporary solution until https://github.com/keycloak/keycloak/issues/12068 is fixed - if (isExpirableEntity) { - queryString += (queryString.contains("WHERE") ? " AND " : " WHERE ") + isNotExpiredIckleWhereClause(); - } - if (!queryParameters.getOrderBy().isEmpty()) { queryString += " ORDER BY " + queryParameters.getOrderBy().stream().map(HotRodMapStorage::toOrderString) .collect(Collectors.joining(", ")); @@ -232,4 +241,12 @@ public class HotRodMapStorage, MapModelCriteriaBuilder.UpdatePredicatesFunc> fieldPredicates = MapFieldPredicates.getPredicates((Class) storedEntityDescriptor.getModelTypeClass()); return new ConcurrentHashMapKeycloakTransaction<>(this, keyConverter, cloner, fieldPredicates); } + + // V must be an instance of ExpirableEntity + // returns null if expiration field is not set + // in certain cases can return 0 or negative number, which needs to be handled carefully when using as ISPN lifespan + private Long getLifespan(V value) { + Long expiration = ((ExpirableEntity) value).getExpiration(); + return expiration != null ? expiration - Time.currentTimeMillis() : null; + } } diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/authSession/HotRodRootAuthenticationSessionEntity.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/authSession/HotRodRootAuthenticationSessionEntity.java index 4504f91797..bd0b2b19c9 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/authSession/HotRodRootAuthenticationSessionEntity.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/authSession/HotRodRootAuthenticationSessionEntity.java @@ -78,7 +78,6 @@ public class HotRodRootAuthenticationSessionEntity extends AbstractHotRodEntity @ProtoField(number = 4) public Long timestamp; - @Basic(sortable = true) @ProtoField(number = 5) public Long expiration; diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/connections/DefaultHotRodConnectionProviderFactory.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/connections/DefaultHotRodConnectionProviderFactory.java index 787fc3e017..70ff5d0657 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/connections/DefaultHotRodConnectionProviderFactory.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/connections/DefaultHotRodConnectionProviderFactory.java @@ -26,12 +26,14 @@ import org.infinispan.commons.marshall.ProtoStreamMarshaller; import org.infinispan.protostream.GeneratedSchema; import org.infinispan.query.remote.client.ProtobufMetadataManagerConstants; import org.jboss.logging.Logger; +import org.keycloak.common.Profile; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.map.storage.hotRod.locking.HotRodLocksUtils; import org.keycloak.models.map.storage.hotRod.common.HotRodEntityDescriptor; import org.keycloak.models.map.storage.hotRod.common.CommonPrimitivesProtoSchemaInitializer; import org.keycloak.models.map.storage.hotRod.common.HotRodVersionUtils; +import org.keycloak.provider.EnvironmentDependentProviderFactory; import java.net.URI; import java.net.URISyntaxException; @@ -49,9 +51,10 @@ import static org.keycloak.models.map.storage.hotRod.common.HotRodVersionUtils.i /** * @author Martin Kanis */ -public class DefaultHotRodConnectionProviderFactory implements HotRodConnectionProviderFactory { +public class DefaultHotRodConnectionProviderFactory implements HotRodConnectionProviderFactory, EnvironmentDependentProviderFactory { public static final String PROVIDER_ID = "default"; + public static final String SCRIPT_CACHE = "___script_cache"; public static final String HOT_ROD_LOCKS_CACHE_NAME = "locks"; private static final String HOT_ROD_INIT_LOCK_NAME = "HOT_ROD_INIT_LOCK"; private static final Logger LOG = Logger.getLogger(DefaultHotRodConnectionProviderFactory.class); @@ -267,4 +270,9 @@ public class DefaultHotRodConnectionProviderFactory implements HotRodConnectionP .nearCacheUseBloomFilter(config.scope(cacheName).getBoolean("nearCacheBloomFilter", config.getBoolean("nearCacheBloomFilter", false))); }; } + + @Override + public boolean isSupported() { + return Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE); + } } diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAdminEventEntity.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAdminEventEntity.java index 22da3a9c25..0eb6e96e3f 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAdminEventEntity.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAdminEventEntity.java @@ -59,7 +59,6 @@ public class HotRodAdminEventEntity extends AbstractHotRodEntity { @ProtoField(number = 2) public String id; - @Basic(sortable = true) @ProtoField(number = 3) public Long expiration; diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAuthEventEntity.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAuthEventEntity.java index 48ef354e3d..df1f03e1cb 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAuthEventEntity.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/events/HotRodAuthEventEntity.java @@ -68,7 +68,6 @@ public class HotRodAuthEventEntity extends AbstractHotRodEntity { @ProtoField(number = 3) public Integer type; - @Basic(sortable = true) @ProtoField(number = 4) public Long expiration; diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/userSession/HotRodUserSessionEntity.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/userSession/HotRodUserSessionEntity.java index 919baf4178..b9f818ce67 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/userSession/HotRodUserSessionEntity.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/userSession/HotRodUserSessionEntity.java @@ -109,7 +109,6 @@ public class HotRodUserSessionEntity extends AbstractHotRodEntity { @ProtoField(number = 12) public Long lastSessionRefresh; - @Basic(sortable = true) @ProtoField(number = 13) public Long expiration; diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/userSession/HotRodUserSessionTransaction.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/userSession/HotRodUserSessionTransaction.java index f8178183d9..0b9990a63c 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/userSession/HotRodUserSessionTransaction.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/userSession/HotRodUserSessionTransaction.java @@ -37,6 +37,7 @@ import org.keycloak.storage.SearchableModelField; import java.util.Collections; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -65,6 +66,7 @@ public class HotRodUserSessionTransaction extends ConcurrentHashMapKeycloakTr } private MapAuthenticatedClientSessionEntity wrapClientSessionEntityToClientSessionAwareDelegate(MapAuthenticatedClientSessionEntity d) { + if (!clientSessionTransaction.exists(d.getId())) return null; return new MapAuthenticatedClientSessionEntityDelegate(new HotRodAuthenticatedClientSessionEntityDelegateProvider(d) { @Override public MapAuthenticatedClientSessionEntity loadClientSessionFromDatabase() { @@ -82,13 +84,15 @@ public class HotRodUserSessionTransaction extends ConcurrentHashMapKeycloakTr Set clientSessions = super.getAuthenticatedClientSessions(); return clientSessions == null ? null : clientSessions.stream() .map(HotRodUserSessionTransaction.this::wrapClientSessionEntityToClientSessionAwareDelegate) + .filter(Objects::nonNull) .collect(Collectors.toSet()); } @Override public Optional getAuthenticatedClientSession(String clientUUID) { return super.getAuthenticatedClientSession(clientUUID) - .map(HotRodUserSessionTransaction.this::wrapClientSessionEntityToClientSessionAwareDelegate); + .map(HotRodUserSessionTransaction.this::wrapClientSessionEntityToClientSessionAwareDelegate) + .filter(Objects::nonNull); } @Override diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml index 1749490245..4220819117 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml @@ -67,6 +67,11 @@ ${project.version} runtime + + org.infinispan + infinispan-tasks-api + ${infinispan.version} + diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/model/infinispan/InfinispanTimeServiceTask.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/model/infinispan/InfinispanTimeServiceTask.java new file mode 100644 index 0000000000..30ef9266ac --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/model/infinispan/InfinispanTimeServiceTask.java @@ -0,0 +1,112 @@ +/* + * 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.infinispan; + +import org.infinispan.commons.logging.Log; +import org.infinispan.commons.logging.LogFactory; +import org.infinispan.commons.time.TimeService; +import org.infinispan.expiration.ExpirationManager; +import org.infinispan.factories.GlobalComponentRegistry; +import org.infinispan.factories.impl.BasicComponentRegistry; +import org.infinispan.manager.EmbeddedCacheManager; +import org.infinispan.tasks.ServerTask; +import org.infinispan.tasks.TaskContext; +import org.infinispan.tasks.TaskExecutionMode; +import org.infinispan.util.EmbeddedTimeService; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +public class InfinispanTimeServiceTask implements ServerTask { + + private static final Log log = LogFactory.getLog(InfinispanTimeServiceTask.class); + private TaskContext context = null; + private static int offset; + + public InfinispanTimeServiceTask() { + log.info("InfinispanTimeServiceTask construction"); + } + + @Override + public String call() { + EmbeddedCacheManager cacheManager = context.getCacheManager(); + Map params = new HashMap(); + if (this.context.getParameters().isPresent()) + params = this.context.getParameters().get(); + if (params.containsKey("timeService")) { + offset = (int) params.get("timeService"); + + // rewire the Time service + GlobalComponentRegistry cr = cacheManager.getGlobalComponentRegistry(); + BasicComponentRegistry bcr = cr.getComponent(BasicComponentRegistry.class); + bcr.replaceComponent(TimeService.class.getName(), KEYCLOAK_TIME_SERVICE, true); + cr.rewire(); + cr.rewireNamedRegistries(); + + // process expiration in all caches + cacheManager.getCacheNames().stream() + .map(cacheManager::getCache) + .filter(Objects::nonNull) + .map(cache -> cache.getAdvancedCache().getExpirationManager()) + .forEach(ExpirationManager::processExpiration); + } + + return "InfinispanTimeServiceTask: Infinispan server time moved by " + offset + " seconds."; + } + + @Override + public String getName() { + log.info("getName() called"); + return "InfinispanTimeServiceTask"; + } + + @Override + public void setTaskContext(TaskContext context) { + this.context = context; + } + + @Override + public TaskExecutionMode getExecutionMode() { + return TaskExecutionMode.ALL_NODES; + } + + public static final TimeService KEYCLOAK_TIME_SERVICE = new EmbeddedTimeService() { + + private long getCurrentTimeMillis() { + return System.currentTimeMillis() + (TimeUnit.SECONDS.toMillis(offset)); + } + + @Override + public long wallClockTime() { + return getCurrentTimeMillis(); + } + + @Override + public long time() { + return TimeUnit.MILLISECONDS.toNanos(getCurrentTimeMillis()); + } + + @Override + public Instant instant() { + return Instant.ofEpochMilli(getCurrentTimeMillis()); + } + }; +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java index 762893a23f..42fe046b5a 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java @@ -17,6 +17,7 @@ package org.keycloak.testsuite.rest; +import org.infinispan.client.hotrod.RemoteCache; import org.jboss.resteasy.annotations.cache.NoCache; import org.keycloak.http.HttpRequest; import org.keycloak.Config; @@ -45,6 +46,13 @@ import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserProvider; import org.keycloak.models.UserSessionModel; +import org.keycloak.models.UserSessionProvider; +import org.keycloak.models.UserSessionSpi; +import org.keycloak.models.map.common.AbstractMapProviderFactory; +import org.keycloak.models.map.storage.MapStorageProvider; +import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory; +import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionProvider; +import org.keycloak.models.map.userSession.MapUserSessionProviderFactory; import org.keycloak.models.sessions.infinispan.changes.sessions.CrossDCLastSessionRefreshStoreFactory; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.ResetTimeOffsetEvent; @@ -238,6 +246,18 @@ public class TestingResourceProvider implements RealmResourceProvider { public Map setTimeOffset(Map time) { int offset = Integer.parseInt(time.get("offset")); + // move time on Hot Rod server if present + // determine usage of Infinispan based on user sessions config + String userSessionProvider = Config.scope(UserSessionSpi.NAME, MapUserSessionProviderFactory.PROVIDER_ID, AbstractMapProviderFactory.CONFIG_STORAGE).get("provider"); + if (Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE) && "hotrod".equals(userSessionProvider)) { + RemoteCache scriptCache = session.getProvider(HotRodConnectionProvider.class).getRemoteCache(DefaultHotRodConnectionProviderFactory.SCRIPT_CACHE); + if (scriptCache != null) { + Map param = new HashMap<>(); + param.put("timeService", offset); + scriptCache.execute("InfinispanTimeServiceTask", param); + } + } + Time.setOffset(offset); // Time offset was restarted diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.infinispan.tasks.ServerTask b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.infinispan.tasks.ServerTask new file mode 100644 index 0000000000..b9ead0dc23 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.infinispan.tasks.ServerTask @@ -0,0 +1 @@ +org.keycloak.testsuite.model.infinispan.InfinispanTimeServiceTask \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index efc72df853..dfd7473766 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -327,6 +327,18 @@ + + copy-testsuite-providers-to-base-testsuite + generate-test-resources + + copy-dependencies + + + org.keycloak.testsuite + integration-arquillian-testsuite-providers + ${project.build.directory}/lib + + @@ -957,6 +969,7 @@ hotrod hotrod ${infinispan.version} + ${project.version} ${keycloak.testsuite.start-hotrod-container} hotrod hotrod diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/HotRodStoreTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/HotRodStoreTestEnricher.java index 1824ab25d4..b8794d10d7 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/HotRodStoreTestEnricher.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/HotRodStoreTestEnricher.java @@ -12,10 +12,11 @@ public class HotRodStoreTestEnricher { public static final boolean HOT_ROD_START_CONTAINER = Boolean.parseBoolean(System.getProperty("keycloak.testsuite.start-hotrod-container", "false")); - private final InfinispanContainer hotRodContainer = new InfinispanContainer(); + private InfinispanContainer hotRodContainer; public void beforeContainerStarted(@Observes(precedence = 1) StartSuiteContainers event) { if (!HOT_ROD_START_CONTAINER) return; + hotRodContainer = new InfinispanContainer(); hotRodContainer.start(); // Add env variable, so it can be picked up by Keycloak @@ -24,6 +25,6 @@ public class HotRodStoreTestEnricher { public void afterSuite(@Observes(precedence = 4) AfterSuite event) { if (!HOT_ROD_START_CONTAINER) return; - hotRodContainer.stop(); + if (hotRodContainer != null) hotRodContainer.stop(); } } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/InfinispanContainer.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/InfinispanContainer.java index df17cd5829..7e6302ab86 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/InfinispanContainer.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/InfinispanContainer.java @@ -21,7 +21,11 @@ import org.jboss.logging.Logger; import org.keycloak.testsuite.arquillian.HotRodStoreTestEnricher; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.MountableFile; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.Duration; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -50,6 +54,18 @@ public class InfinispanContainer extends GenericContainer { withEnv("USER", USERNAME); withEnv("PASS", PASSWORD); withNetworkMode("host"); + + Path dir = Path.of(Path.of("").toAbsolutePath() + "/target/lib"); + String projectVersion = System.getProperty("project.version"); + Path timeTaskPath; + try { + timeTaskPath = Files.find(dir, 1, (path, attr) -> path.toString() + .endsWith("integration-arquillian-testsuite-providers-" + projectVersion + ".jar")).findFirst().orElse(null); + } catch (IOException e) { + throw new RuntimeException(e); + } + MountableFile mountableFile = MountableFile.forHostPath(timeTaskPath, 0666); + withCopyFileToContainer(mountableFile, "/opt/infinispan/server/lib/integration-arquillian-testsuite-providers.jar"); withStartupTimeout(Duration.ofMinutes(5)); waitingFor(Wait.forLogMessage(".*Infinispan Server.*started in.*", 1)); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java index c5fe4e06b2..9f3a56ca65 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java @@ -644,6 +644,7 @@ public abstract class AbstractKeycloakTest { /** * Sets time offset in seconds that will be added to Time.currentTime() and Time.currentTimeMillis() both for client and server. + * Moves time on the remote Infinispan server as well if the HotRod storage is used. * * @param offset */ diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/TimeOffsetTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/TimeOffsetTest.java new file mode 100644 index 0000000000..ce0b631f60 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/TimeOffsetTest.java @@ -0,0 +1,66 @@ +/* + * 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.admin; + +import org.keycloak.Config; +import org.keycloak.common.util.Time; +import org.keycloak.events.Event; +import org.keycloak.events.EventStoreProvider; +import org.keycloak.events.EventStoreSpi; +import org.keycloak.events.EventType; +import org.keycloak.events.jpa.JpaEventStoreProviderFactory; +import org.keycloak.models.RealmModel; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TimeOffsetTest extends AbstractAdminTest { + + @Test + public void testOffset() { + String realmId = adminClient.realm(REALM_NAME).toRepresentation().getId(); + testingClient.server().run(session -> { + RealmModel realm = session.realms().getRealmByName(REALM_NAME); + realm.setEventsExpiration(5); + EventStoreProvider provider = session.getProvider(EventStoreProvider.class); + + Event e = new Event(); + e.setType(EventType.LOGIN); + e.setTime(Time.currentTimeMillis()); + e.setRealmId(realmId); + provider.onEvent(e); + }); + + testingClient.server().run(session -> { + EventStoreProvider provider = session.getProvider(EventStoreProvider.class); + assertEquals(1, provider.createQuery().realm(realmId).getResultStream().count()); + }); + + setTimeOffset(5); + + // legacy store requires manual trigger of expired events removal + String eventStoreProvider = testingClient.server().fetch(session -> Config.getProvider(EventStoreSpi.NAME), String.class); + if (eventStoreProvider.equals(JpaEventStoreProviderFactory.ID)) { + testingClient.testing().clearExpiredEvents(); + } + + testingClient.server().run(session -> { + EventStoreProvider provider = session.getProvider(EventStoreProvider.class); + assertEquals(0, provider.createQuery().realm(realmId).getResultStream().count()); + }); + } +} diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index f128a34bdc..b869f78bfa 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -179,6 +179,7 @@ org.jboss.logmanager.LogManager log4j ${infinispan.version} + ${project.version} @@ -207,7 +208,24 @@ - + + + maven-dependency-plugin + + + copy-testsuite-providers-to-model-testsuite + generate-test-resources + + copy-dependencies + + + org.keycloak.testsuite + integration-arquillian-testsuite-providers + ${project.build.directory}/lib + + + + diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java index 8b57992603..61142e2089 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/KeycloakModelTest.java @@ -16,6 +16,7 @@ */ package org.keycloak.testsuite.model; +import org.infinispan.client.hotrod.RemoteCache; import org.junit.Assert; import org.keycloak.Config.Scope; import org.keycloak.authorization.AuthorizationSpi; @@ -45,6 +46,8 @@ import org.keycloak.models.DeploymentStateSpi; import org.keycloak.models.UserLoginFailureSpi; import org.keycloak.models.UserSessionSpi; import org.keycloak.models.UserSpi; +import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory; +import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionProvider; import org.keycloak.models.locking.GlobalLockProviderSpi; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.PostMigrationEvent; @@ -63,7 +66,7 @@ import java.lang.management.LockInfo; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.util.Arrays; -import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -81,7 +84,6 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -527,17 +529,17 @@ public abstract class KeycloakModelTest { @Before public final void createEnvironment() { - Time.setOffset(0); + setTimeOffset(0); USE_DEFAULT_FACTORY = isUseSameKeycloakSessionFactoryForAllThreads(); KeycloakModelUtils.runJobInTransaction(getFactory(), this::createEnvironment); } @After public final void cleanEnvironment() { - Time.setOffset(0); if (getFactory() == null) { reinitializeKeycloakSessionFactory(); } + setTimeOffset(0); KeycloakModelUtils.runJobInTransaction(getFactory(), this::cleanEnvironment); } @@ -637,4 +639,24 @@ public abstract class KeycloakModelTest { return realm; } + /** + * Moves time on the Keycloak server as well as on the remote Infinispan server if the Infinispan is used. + * @param seconds time offset in seconds by which Keycloak (and Infinispan) server time is moved + */ + protected void setTimeOffset(int seconds) { + inComittedTransaction(session -> { + // move time on Hot Rod server if present + HotRodConnectionProvider hotRodConnectionProvider = session.getProvider(HotRodConnectionProvider.class); + if (hotRodConnectionProvider != null) { + RemoteCache scriptCache = hotRodConnectionProvider.getRemoteCache(DefaultHotRodConnectionProviderFactory.SCRIPT_CACHE); + if (scriptCache != null) { + Map param = new HashMap<>(); + param.put("timeService", seconds); + Object returnFromTask = scriptCache.execute("InfinispanTimeServiceTask", param); + LOG.info(returnFromTask); + } + } + Time.setOffset(seconds); + }); + } } diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/MigrationModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/MigrationModelTest.java index b3269771e7..0286350d39 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/MigrationModelTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/MigrationModelTest.java @@ -22,7 +22,6 @@ import org.jboss.logging.Logger; import org.junit.Assert; import org.junit.Test; import org.keycloak.common.Version; -import org.keycloak.common.util.Time; import org.keycloak.connections.jpa.JpaConnectionProvider; import org.keycloak.migration.MigrationModel; import org.keycloak.migration.ModelVersion; @@ -72,12 +71,12 @@ public class MigrationModelTest extends KeycloakModelTest { Assert.assertEquals(currentVersion, m.getStoredVersion()); Assert.assertEquals(m.getResourcesTag(), l.get(0).getId()); - Time.setOffset(-60000); + setTimeOffset(-60000); session.getProvider(DeploymentStateProvider.class).getMigrationModel().setStoredVersion("6.0.0"); em.flush(); - Time.setOffset(0); + setTimeOffset(0); l = em.createQuery("select m from MigrationModelEntity m ORDER BY m.updatedTime DESC", MigrationModelEntity.class).getResultList(); Assert.assertEquals(2, l.size()); diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/TimeOffsetTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/TimeOffsetTest.java new file mode 100644 index 0000000000..2dbaa3440f --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/TimeOffsetTest.java @@ -0,0 +1,82 @@ +/* + * 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; + +import org.junit.Test; +import org.keycloak.common.util.Time; +import org.keycloak.events.Event; +import org.keycloak.events.EventStoreProvider; +import org.keycloak.events.EventType; +import org.keycloak.models.Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.provider.ProviderFactory; + +import static org.junit.Assert.assertEquals; + +@RequireProvider(EventStoreProvider.class) +public class TimeOffsetTest extends KeycloakModelTest { + + private String realmId; + + @Override + protected void createEnvironment(KeycloakSession s) { + RealmModel r = s.realms().createRealm("realm"); + r.setDefaultRole(s.roles().addRealmRole(r, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + r.getName())); + r.setEventsExpiration(5); + realmId = r.getId(); + } + + @Override + protected void cleanEnvironment(KeycloakSession s) { + s.realms().removeRealm(realmId); + } + + @Test + public void testOffset() { + withRealm(realmId, (session, realmModel) -> { + EventStoreProvider provider = session.getProvider(EventStoreProvider.class); + + Event e = new Event(); + e.setType(EventType.LOGIN); + e.setRealmId(realmId); + e.setTime(Time.currentTimeMillis()); + provider.onEvent(e); + return null; + }); + + withRealm(realmId, (session, realmModel) -> { + EventStoreProvider provider = session.getProvider(EventStoreProvider.class); + assertEquals(1, provider.createQuery().realm(realmId).getResultStream().count()); + + setTimeOffset(5); + + // legacy store requires explicit expiration of expired events + ProviderFactory providerFactory = session.getKeycloakSessionFactory().getProviderFactory(EventStoreProvider.class); + if ("jpa".equals(providerFactory.getId())) { + provider.clearExpiredEvents(); + } + return null; + }); + + withRealm(realmId, (session, realmModel) -> { + EventStoreProvider provider = session.getProvider(EventStoreProvider.class); + assertEquals(0, provider.createQuery().realm(realmId).getResultStream().count()); + return null; + }); + } +} diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/events/EventQueryTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/events/EventQueryTest.java index 426397053c..f603bf125b 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/events/EventQueryTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/events/EventQueryTest.java @@ -17,7 +17,6 @@ package org.keycloak.testsuite.model.events; import org.keycloak.common.ClientConnection; -import org.keycloak.common.util.Time; import org.keycloak.events.Event; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventStoreProvider; @@ -159,7 +158,7 @@ public class EventQueryTest extends KeycloakModelTest { return null; }); - Time.setOffset(10); + setTimeOffset(10); try { withRealm(realmId, (session, realm) -> { @@ -173,7 +172,7 @@ public class EventQueryTest extends KeycloakModelTest { return null; }); } finally { - Time.setOffset(0); + setTimeOffset(0); } diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/AuthenticationSessionTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/AuthenticationSessionTest.java index c71a5f8d32..6aa535d78f 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/AuthenticationSessionTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/AuthenticationSessionTest.java @@ -20,7 +20,6 @@ package org.keycloak.testsuite.model.session; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; -import org.keycloak.common.util.Time; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; @@ -72,7 +71,7 @@ public class AuthenticationSessionTest extends KeycloakModelTest { ClientModel client = realm.getClientByClientId("test-app"); return IntStream.range(0, 300) .mapToObj(i -> { - Time.setOffset(i); + setTimeOffset(i); return ras.createAuthenticationSession(client); }) .map(AuthenticationSessionModel::getTabId) @@ -184,7 +183,7 @@ public class AuthenticationSessionTest extends KeycloakModelTest { RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().getRootAuthenticationSession(realm, rootAuthSessionId.get()); Assert.assertNotNull(rootAuthSession); - Time.setOffset(1900); + setTimeOffset(1900); return null; }); diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionExpirationTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionExpirationTest.java index 93746030a2..743b5e81c5 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionExpirationTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionExpirationTest.java @@ -18,7 +18,6 @@ package org.keycloak.testsuite.model.session; import org.junit.Test; -import org.keycloak.common.util.Time; import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -67,9 +66,8 @@ public class UserSessionExpirationTest extends KeycloakModelTest { assertThat(withRealm(realmId, (session, realm) -> session.sessions().getUserSession(realm, uSId)), notNullValue()); - Time.setOffset(5); + setTimeOffset(5); assertThat(withRealm(realmId, (session, realm) -> session.sessions().getUserSession(realm, uSId)), nullValue()); - } } diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionPersisterProviderTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionPersisterProviderTest.java index 70c4667fa8..dfacce2553 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionPersisterProviderTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionPersisterProviderTest.java @@ -432,7 +432,7 @@ public class UserSessionPersisterProviderTest extends KeycloakModelTest { for (int i = 0; i < USER_SESSION_COUNT; i++) { // Having different offsets for each session (to ensure that lastSessionRefresh is also different) - Time.setOffset(i); + setTimeOffset(i); UserSessionModel userSession = session.sessions().createUserSession(realm, user, "user1", "127.0.0.1", "form", true, null, null); createClientSession(session, realmId, realm.getClientByClientId("test-app"), userSession, "http://redirect", "state"); @@ -464,6 +464,7 @@ public class UserSessionPersisterProviderTest extends KeycloakModelTest { } return null; }); + } @Test @@ -495,7 +496,7 @@ public class UserSessionPersisterProviderTest extends KeycloakModelTest { persister.updateLastSessionRefreshes(realm, lastSessionRefresh, Collections.singleton(userSession1[0].getId()), true); // Increase time offset - 40 days - Time.setOffset(3456000); + setTimeOffset(3456000); try { // Run expiration thread persister.removeExpired(realm); @@ -507,7 +508,7 @@ public class UserSessionPersisterProviderTest extends KeycloakModelTest { } finally { // Cleanup - Time.setOffset(0); + setTimeOffset(0); session.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent()); } }); diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderModelTest.java index eadef01bfe..ebc511238f 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderModelTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderModelTest.java @@ -20,7 +20,6 @@ import org.hamcrest.Matchers; import org.infinispan.client.hotrod.RemoteCache; import org.junit.Assert; import org.junit.Test; -import org.keycloak.common.util.Time; import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; @@ -193,7 +192,7 @@ public class UserSessionProviderModelTest extends KeycloakModelTest { clientSession.setTimestamp(1); }); } else { - Time.setOffset(1000); + setTimeOffset(1000); } }); @@ -211,7 +210,7 @@ public class UserSessionProviderModelTest extends KeycloakModelTest { }); }); } finally { - Time.setOffset(0); + setTimeOffset(0); kcSession.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent()); if (timer != null && timerTaskCtx != null) { timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME); diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderOfflineModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderOfflineModelTest.java index e374901c1b..3d0b299404 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderOfflineModelTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionProviderOfflineModelTest.java @@ -17,7 +17,6 @@ package org.keycloak.testsuite.model.session; -import org.hamcrest.Matchers; import org.infinispan.Cache; import org.junit.Assert; import org.junit.Assume; @@ -51,9 +50,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -163,7 +160,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest { // sessions are in persister too Assert.assertEquals(3, persister.getUserSessionsCount(true)); - Time.setOffset(300); + setTimeOffset(300); log.infof("Set time offset to 300. Time is: %d", Time.currentTime()); // Set lastSessionRefresh to currentSession[0] to 0 @@ -178,7 +175,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest { int timeOffset = 1728000 + (i * 86400); RealmModel realm = session.realms().getRealm(realmId); - Time.setOffset(timeOffset); + setTimeOffset(timeOffset); log.infof("Set time offset to %d. Time is: %d", timeOffset, Time.currentTime()); UserSessionModel session0 = session.sessions().getOfflineUserSession(realm, origSessions[0].getId()); @@ -192,7 +189,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest { persister = session.getProvider(UserSessionPersisterProvider.class); // Increase timeOffset - 40 days - Time.setOffset(3456000); + setTimeOffset(3456000); log.infof("Set time offset to 3456000. Time is: %d", Time.currentTime()); // Expire and ensure that all sessions despite session0 were removed @@ -211,7 +208,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest { Assert.assertEquals(1, persister.getUserSessionsCount(true)); // Expire everything and assert nothing found - Time.setOffset(7000000); + setTimeOffset(7000000); persister.removeExpired(realm); }); @@ -228,7 +225,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest { }); } finally { - Time.setOffset(0); + setTimeOffset(0); kcSession.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent()); if (timer != null) { timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME); @@ -278,7 +275,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest { persister = session.getProvider(UserSessionPersisterProvider.class); // Expire everything except offline client sessions - Time.setOffset(7000000); + setTimeOffset(7000000); persister.removeExpired(realm); }); @@ -300,7 +297,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest { }); } finally { - Time.setOffset(0); + setTimeOffset(0); kcSession.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent()); if (timer != null) { timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME); diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/singleUseObject/SingleUseObjectModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/singleUseObject/SingleUseObjectModelTest.java index 075b6f7b2f..714c6908e6 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/singleUseObject/SingleUseObjectModelTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/singleUseObject/SingleUseObjectModelTest.java @@ -60,7 +60,6 @@ public class SingleUseObjectModelTest extends KeycloakModelTest { @Override public void cleanEnvironment(KeycloakSession s) { - Time.setOffset(0); s.realms().removeRealm(realmId); } @@ -103,7 +102,7 @@ public class SingleUseObjectModelTest extends KeycloakModelTest { Assert.assertNotNull(notes); Assert.assertEquals("bar", notes.get("foo")); - Time.setOffset(70); + setTimeOffset(70); notes = singleUseObjectProvider.get(key.serializeKey()); Assert.assertNull(notes); @@ -154,7 +153,7 @@ public class SingleUseObjectModelTest extends KeycloakModelTest { Map actualNotes = singleUseStore.get(key); assertThat(actualNotes, Matchers.anEmptyMap()); - Time.setOffset(70); + setTimeOffset(70); Assert.assertNull(singleUseStore.get(key)); });