Disable Infinispan for map storage and avoid the component factory when creating a realm independent provider factory

Provide startup time in UserSessionProvider independent of Infinispan,
cleanup code that is not necessary for the map storage as it isn't using Clustering.
Move classes to the legacy module.

Closes #12972
This commit is contained in:
Alexander Schwartz 2022-07-20 16:11:59 +02:00 committed by Hynek Mlnařík
parent 0eb10f2128
commit cb81a17611
27 changed files with 133 additions and 113 deletions

View file

@ -30,6 +30,7 @@ import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.cluster.ClusterProvider;
import org.keycloak.cluster.ClusterProviderFactory;
import org.keycloak.common.Profile;
import org.keycloak.common.util.Retry;
import org.keycloak.common.util.Time;
import org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory;
@ -38,6 +39,7 @@ import org.keycloak.connections.infinispan.TopologyInfo;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.connections.infinispan.InfinispanUtil;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import java.io.Serializable;
import java.util.Collection;
@ -54,7 +56,7 @@ import java.util.stream.Collectors;
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class InfinispanClusterProviderFactory implements ClusterProviderFactory {
public class InfinispanClusterProviderFactory implements ClusterProviderFactory, EnvironmentDependentProviderFactory {
public static final String PROVIDER_ID = "infinispan";
@ -190,6 +192,10 @@ public class InfinispanClusterProviderFactory implements ClusterProviderFactory
return PROVIDER_ID;
}
@Override
public boolean isSupported() {
return !Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
}
@Listener
public class ViewChangeListener {

View file

@ -38,6 +38,7 @@ import org.keycloak.cluster.ClusterEvent;
import org.keycloak.cluster.ClusterProvider;
import org.keycloak.cluster.ManagedCacheManagerProvider;
import org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory;
import org.keycloak.common.Profile;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.cache.infinispan.ClearCacheEvent;
@ -45,6 +46,7 @@ import org.keycloak.models.cache.infinispan.events.RealmRemovedEvent;
import org.keycloak.models.cache.infinispan.events.RealmUpdatedEvent;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.PostMigrationEvent;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.provider.InvalidationHandler.ObjectType;
import org.keycloak.provider.ProviderEvent;
@ -62,7 +64,7 @@ import static org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderF
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultInfinispanConnectionProviderFactory implements InfinispanConnectionProviderFactory {
public class DefaultInfinispanConnectionProviderFactory implements InfinispanConnectionProviderFactory, EnvironmentDependentProviderFactory {
protected static final Logger logger = Logger.getLogger(DefaultInfinispanConnectionProviderFactory.class);
@ -490,4 +492,9 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
}
});
}
@Override
public boolean isSupported() {
return !Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
}
}

View file

@ -19,8 +19,10 @@ package org.keycloak.models.sessions.infinispan;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.common.Profile;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.sessions.StickySessionEncoderProvider;
@ -32,7 +34,7 @@ import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class InfinispanStickySessionEncoderProviderFactory implements StickySessionEncoderProviderFactory {
public class InfinispanStickySessionEncoderProviderFactory implements StickySessionEncoderProviderFactory, EnvironmentDependentProviderFactory {
private static final Logger log = Logger.getLogger(InfinispanStickySessionEncoderProviderFactory.class);
@ -87,4 +89,9 @@ public class InfinispanStickySessionEncoderProviderFactory implements StickySess
.add()
.build();
}
@Override
public boolean isSupported() {
return !Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
}
}

View file

@ -709,6 +709,12 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
public void close() {
}
@Override
public int getStartupTime(RealmModel realm) {
// TODO: take realm.getNotBefore() into account?
return session.getProvider(ClusterProvider.class).getClusterStartupTime();
}
protected void removeUserSession(UserSessionEntity sessionEntity, boolean offline) {
InfinispanChangelogBasedTransaction<String, UserSessionEntity> userSessionUpdateTx = getTransaction(offline);
InfinispanChangelogBasedTransaction<UUID, AuthenticatedClientSessionEntity> clientSessionUpdateTx = getClientSessionTransaction(offline);

View file

@ -23,6 +23,7 @@ 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.Time;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
@ -58,6 +59,7 @@ import org.keycloak.models.sessions.infinispan.util.SessionTimeouts;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.PostMigrationEvent;
import org.keycloak.models.utils.ResetTimeOffsetEvent;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderEventListener;
@ -67,7 +69,7 @@ import java.util.UUID;
import java.util.function.BiFunction;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
public class InfinispanUserSessionProviderFactory implements UserSessionProviderFactory {
public class InfinispanUserSessionProviderFactory implements UserSessionProviderFactory, EnvironmentDependentProviderFactory {
private static final Logger log = Logger.getLogger(InfinispanUserSessionProviderFactory.class);
@ -350,5 +352,10 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
public int order() {
return PROVIDER_PRIORITY;
}
@Override
public boolean isSupported() {
return !Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
}
}

View file

@ -22,3 +22,4 @@ org.keycloak.storage.client.ClientStorageProviderSpi
org.keycloak.storage.group.GroupStorageProviderSpi
org.keycloak.storage.clientscope.ClientScopeStorageProviderSpi
org.keycloak.models.session.UserSessionPersisterSpi
org.keycloak.cluster.ClusterSpi

View file

@ -54,8 +54,6 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
private final KeycloakSession session;
protected final MapKeycloakTransaction<MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> tx;
private static final String AUTHENTICATION_SESSION_EVENTS = "AUTHENTICATION_SESSION_EVENTS";
public MapRootAuthenticationSessionProvider(KeycloakSession session, MapStorage<MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> sessionStore) {
this.session = session;
this.tx = sessionStore.createTransaction(session);
@ -165,15 +163,6 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
return;
}
Objects.requireNonNull(authNotesFragment, "The provided authentication's notes map can't be null!");
ClusterProvider cluster = session.getProvider(ClusterProvider.class);
cluster.notify(
AUTHENTICATION_SESSION_EVENTS,
MapAuthenticationSessionAuthNoteUpdateEvent.create(compoundId.getRootSessionId(), compoundId.getTabId(),
compoundId.getClientUUID(), authNotesFragment),
true,
ClusterProvider.DCNotify.ALL_BUT_LOCAL_DC
);
}
@Override

View file

@ -18,8 +18,18 @@
package org.keycloak.models.map.authorization;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.PermissionTicket;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
import org.keycloak.models.map.storage.MapStorage;
@ -35,8 +45,9 @@ public class MapAuthorizationStore implements StoreFactory {
private final MapPermissionTicketStore permissionTicketStore;
private boolean readOnly;
@SuppressWarnings("unchecked")
public MapAuthorizationStore(KeycloakSession session, MapStorage permissionTicketStore, MapStorage policyStore, MapStorage resourceServerStore, MapStorage resourceStore, MapStorage scopeStore, AuthorizationProvider provider) {
public MapAuthorizationStore(KeycloakSession session, MapStorage<MapPermissionTicketEntity, PermissionTicket> permissionTicketStore,
MapStorage<MapPolicyEntity, Policy> policyStore, MapStorage<MapResourceServerEntity, ResourceServer> resourceServerStore,
MapStorage<MapResourceEntity, Resource> resourceStore, MapStorage<MapScopeEntity, Scope> scopeStore, AuthorizationProvider provider) {
this.permissionTicketStore = new MapPermissionTicketStore(session, permissionTicketStore, provider);
this.policyStore = new MapPolicyStore(session, policyStore, provider);
this.resourceServerStore = new MapResourceServerStore(session, resourceServerStore, provider);

View file

@ -17,7 +17,6 @@
package org.keycloak.models.map.authorization;
import java.util.concurrent.atomic.AtomicInteger;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.PermissionTicket;
@ -31,18 +30,20 @@ import org.keycloak.common.Profile;
import org.keycloak.component.AmphibianProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
import org.keycloak.models.map.common.AbstractMapProviderFactory;
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.MapStorageSpi;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.provider.InvalidationHandler;
import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE;
import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.RESOURCE_SERVER_BEFORE_REMOVE;
import static org.keycloak.models.map.common.AbstractMapProviderFactory.uniqueCounter;
import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory;
/**
* @author mhajas
@ -60,16 +61,14 @@ public class MapAuthorizationStoreFactory implements AmphibianProviderFactory<St
if (authzStore != null) return authzStore;
MapStorageProviderFactory storageProviderFactory = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
MapStorageProvider.class, storageConfigScope, MapStorageSpi.NAME);
final MapStorageProvider mapStorageProvider = storageProviderFactory.create(session);
final MapStorageProvider mapStorageProvider = AbstractMapProviderFactory.getProviderFactoryOrComponentFactory(session, storageConfigScope).create(session);
AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class);
MapStorage permissionTicketStore = mapStorageProvider.getStorage(PermissionTicket.class);
MapStorage policyStore = mapStorageProvider.getStorage(Policy.class);
MapStorage resourceServerStore = mapStorageProvider.getStorage(ResourceServer.class);
MapStorage resourceStore = mapStorageProvider.getStorage(Resource.class);
MapStorage scopeStore = mapStorageProvider.getStorage(Scope.class);
MapStorage<MapPermissionTicketEntity, PermissionTicket> permissionTicketStore = mapStorageProvider.getStorage(PermissionTicket.class);
MapStorage<MapPolicyEntity, Policy> policyStore = mapStorageProvider.getStorage(Policy.class);
MapStorage<MapResourceServerEntity, ResourceServer> resourceServerStore = mapStorageProvider.getStorage(ResourceServer.class);
MapStorage<MapResourceEntity, Resource> resourceStore = mapStorageProvider.getStorage(Resource.class);
MapStorage<MapScopeEntity, Scope> scopeStore = mapStorageProvider.getStorage(Scope.class);
authzStore = new MapAuthorizationStore(session,
permissionTicketStore,

View file

@ -20,9 +20,9 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.keycloak.Config.Scope;
import org.keycloak.common.Profile;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelException;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.MapStorageProvider;
import org.keycloak.models.map.storage.MapStorageSpi;
import org.keycloak.component.AmphibianProviderFactory;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
@ -31,8 +31,6 @@ import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.jboss.logging.Logger;
import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory;
/**
*
* @author hmlnarik
@ -108,13 +106,34 @@ public abstract class AbstractMapProviderFactory<T extends Provider, V extends A
}
protected MapStorage<V, M> getStorage(KeycloakSession session) {
ProviderFactory<MapStorageProvider> storageProviderFactory = getComponentFactory(session.getKeycloakSessionFactory(),
MapStorageProvider.class, storageConfigScope, MapStorageSpi.NAME);
ProviderFactory<MapStorageProvider> storageProviderFactory = getProviderFactoryOrComponentFactory(session, storageConfigScope);
final MapStorageProvider factory = storageProviderFactory.create(session);
return factory.getStorage(modelType);
}
public static ProviderFactory<MapStorageProvider> getProviderFactoryOrComponentFactory(KeycloakSession session, Scope storageConfigScope) {
ProviderFactory<MapStorageProvider> storageProviderFactory;
if (!hasRealmSpecificStorage(session, storageConfigScope)) {
String provider = storageConfigScope.get("provider");
if (provider == null) {
storageProviderFactory = session.getKeycloakSessionFactory().getProviderFactory(MapStorageProvider.class);
} else {
storageProviderFactory = session.getKeycloakSessionFactory().getProviderFactory(MapStorageProvider.class, provider);
}
} else {
// If this is being implemented, make sure that the factory is being closed eventually.
// When no cluster provider is available, the componentFactory will not be cached and a new instance is being returned all the time
// when calling `getComponentFactory(session.getKeycloakSessionFactory(), MapStorageProvider.class, storageConfigScope, MapStorageSpi.NAME)`.
throw new ModelException("not supported yet");
}
return storageProviderFactory;
}
private static boolean hasRealmSpecificStorage(KeycloakSession session, Scope storageConfigScope) {
// Once there is functionality for a realm-specific storage, implement the logic on how to detect it here.
return false;
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}

View file

@ -30,14 +30,11 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.map.common.AbstractMapProviderFactory;
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.MapStorageSpi;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.provider.InvalidationHandler;
import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE;
import static org.keycloak.models.map.common.AbstractMapProviderFactory.uniqueCounter;
import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory;
public class MapEventStoreProviderFactory implements AmphibianProviderFactory<EventStoreProvider>, EnvironmentDependentProviderFactory, EventStoreProviderFactory, InvalidationHandler {
@ -62,15 +59,11 @@ public class MapEventStoreProviderFactory implements AmphibianProviderFactory<Ev
MapEventStoreProvider provider = session.getAttribute(uniqueKey, MapEventStoreProvider.class);
if (provider != null) return provider;
MapStorageProviderFactory storageProviderFactoryAe = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
MapStorageProvider.class, storageConfigScopeAdminEvents, MapStorageSpi.NAME);
final MapStorageProvider factoryAe = storageProviderFactoryAe.create(session);
MapStorage adminEventsStore = factoryAe.getStorage(AdminEvent.class);
final MapStorageProvider factoryAe = AbstractMapProviderFactory.getProviderFactoryOrComponentFactory(session, storageConfigScopeAdminEvents).create(session);
MapStorage<MapAdminEventEntity, AdminEvent> adminEventsStore = factoryAe.getStorage(AdminEvent.class);
MapStorageProviderFactory storageProviderFactoryLe = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
MapStorageProvider.class, storageConfigScopeLoginEvents, MapStorageSpi.NAME);
final MapStorageProvider factoryLe = storageProviderFactoryLe.create(session);
MapStorage loginEventsStore = factoryLe.getStorage(Event.class);
final MapStorageProvider factoryLe = AbstractMapProviderFactory.getProviderFactoryOrComponentFactory(session, storageConfigScopeLoginEvents).create(session);
MapStorage<MapAuthEventEntity, Event> loginEventsStore = factoryLe.getStorage(Event.class);
provider = new MapEventStoreProvider(session, loginEventsStore, adminEventsStore);
session.setAttribute(uniqueKey, provider);

View file

@ -523,6 +523,11 @@ public class MapUserSessionProvider implements UserSessionProvider {
}
@Override
public int getStartupTime(RealmModel realm) {
return realm.getNotBefore();
}
/**
* Removes all online and offline user sessions that belong to the provided {@link RealmModel}.
* @param realm

View file

@ -218,24 +218,6 @@ public class StorageOptions {
.buildTime(true)
.build();
public static final Option<Boolean> STORAGE_CACHE_ENABLED = new OptionBuilder<>("cache-enabled", Boolean.class)
.category(OptionCategory.STORAGE)
.hidden()
.buildTime(true)
.build();
public static final Option<Boolean> STORAGE_CACHE_CLUSTER_ENABLED = new OptionBuilder<>("cache-cluster-enabled", Boolean.class)
.category(OptionCategory.STORAGE)
.hidden()
.buildTime(true)
.build();
public static final Option<String> STORAGE_CACHE_STICK_SESSION_ENABLED = new OptionBuilder<>("cache-stick-session-enabled", String.class)
.category(OptionCategory.STORAGE)
.hidden()
.buildTime(true)
.build();
public static final Option<String> STORAGE_CACHE_REALM_ENABLED = new OptionBuilder<>("cache-realm-enabled", String.class)
.category(OptionCategory.STORAGE)
.hidden()
@ -290,12 +272,6 @@ public class StorageOptions {
.buildTime(true)
.build();
public static final Option<String> STORAGE_CACHE_COMPONENT_FACTORY = new OptionBuilder<>("cache-component-factory-cache", String.class)
.category(OptionCategory.STORAGE)
.hidden()
.buildTime(true)
.build();
public static final Option<String> STORAGE_LEGACY_SESSION_SUPPORT = new OptionBuilder<>("storage-legacy-session-support", String.class)
.category(OptionCategory.STORAGE)
.hidden()

View file

@ -229,12 +229,6 @@ final class StoragePropertyMappers {
.transformer(StoragePropertyMappers::resolveMapStorageProvider)
.paramLabel("type")
.build(),
fromOption(StorageOptions.STORAGE_CACHE_COMPONENT_FACTORY)
.to("kc.spi-component-factory-default-caching-forced")
.mapFrom("storage")
.transformer(StoragePropertyMappers::isForceComponentFactoryCache)
.paramLabel("type")
.build(),
fromOption(StorageOptions.STORAGE_PUBLIC_KEY_STORE)
.to("kc.spi-public-key-storage-map-storage-provider")
.mapFrom("storage")
@ -247,24 +241,6 @@ final class StoragePropertyMappers {
.transformer(StoragePropertyMappers::isLegacyStoreEnabled)
.paramLabel(Boolean.TRUE + "|" + Boolean.FALSE)
.build(),
fromOption(StorageOptions.STORAGE_CACHE_ENABLED)
.to("kc.spi-connections-infinispan-default-enabled")
.mapFrom("storage")
.transformer(StoragePropertyMappers::isLegacyStoreEnabled)
.paramLabel(Boolean.TRUE + "|" + Boolean.FALSE)
.build(),
fromOption(StorageOptions.STORAGE_CACHE_CLUSTER_ENABLED)
.to("kc.spi-cluster-infinispan-enabled")
.mapFrom("storage")
.transformer(StoragePropertyMappers::isLegacyStoreEnabled)
.paramLabel(Boolean.TRUE + "|" + Boolean.FALSE)
.build(),
fromOption(StorageOptions.STORAGE_CACHE_STICK_SESSION_ENABLED)
.to("kc.spi-sticky-session-encoder-infinispan-enabled")
.mapFrom("storage")
.transformer(StoragePropertyMappers::isLegacyStoreEnabled)
.paramLabel(Boolean.TRUE + "|" + Boolean.FALSE)
.build(),
fromOption(StorageOptions.STORAGE_CACHE_CLEAR_REALM)
.to("kc.spi-admin-realm-restapi-extension-clear-realm-cache-enabled")
.mapFrom("storage")

View file

@ -27,7 +27,9 @@ import java.util.concurrent.Future;
* Various utils related to clustering and concurrent tasks on cluster nodes
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
* @deprecated This is only available when the legacy store is enabled. Support for this will be eventually removed.
*/
@Deprecated
public interface ClusterProvider extends Provider {
/**

View file

@ -61,7 +61,6 @@ org.keycloak.authentication.ClientAuthenticatorSpi
org.keycloak.authentication.RequiredActionSpi
org.keycloak.authentication.FormAuthenticatorSpi
org.keycloak.authentication.FormActionSpi
org.keycloak.cluster.ClusterSpi
org.keycloak.authorization.policy.provider.PolicySpi
org.keycloak.authorization.store.StoreFactorySpi
org.keycloak.authorization.AuthorizationSpi

View file

@ -277,5 +277,5 @@ public interface UserSessionProvider extends Provider {
void close();
int getStartupTime(RealmModel realm);
}

View file

@ -27,7 +27,6 @@ import org.keycloak.TokenVerifier;
import org.keycloak.authentication.authenticators.util.AcrStore;
import org.keycloak.broker.oidc.OIDCIdentityProvider;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.cluster.ClusterProvider;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.Profile;
import org.keycloak.common.VerificationException;
@ -53,6 +52,7 @@ import org.keycloak.models.SingleUseObjectProvider;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.protocol.ProtocolMapperUtils;
@ -436,12 +436,12 @@ public class TokenManager {
// Will throw OAuthErrorException if validation fails
private void validateTokenReuse(KeycloakSession session, RealmModel realm, AccessToken refreshToken,
AuthenticatedClientSessionModel clientSession, boolean refreshFlag) throws OAuthErrorException {
int clusterStartupTime = session.getProvider(ClusterProvider.class).getClusterStartupTime();
int startupTime = session.getProvider(UserSessionProvider.class).getStartupTime(realm);
if (clientSession.getCurrentRefreshToken() != null
&& !refreshToken.getId().equals(clientSession.getCurrentRefreshToken())
&& refreshToken.getIssuedAt() < clientSession.getTimestamp()
&& clusterStartupTime <= clientSession.getTimestamp()) {
&& startupTime <= clientSession.getTimestamp()) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token");
}

View file

@ -44,15 +44,18 @@ public class InfinispanTestUtil {
throw new IllegalStateException("Calling setTestingTimeService when testing TimeService was already set");
}
logger.info("Will set KeycloakIspnTimeService to the infinispan cacheManager");
InfinispanConnectionProvider ispnProvider = session.getProvider(InfinispanConnectionProvider.class);
if (ispnProvider != null) {
logger.info("Will set KeycloakIspnTimeService to the infinispan cacheManager");
EmbeddedCacheManager cacheManager = ispnProvider.getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME).getCacheManager();
origTimeService = setTimeServiceToKeycloakTime(cacheManager);
}
}
public static void revertTimeService() {
public static void revertTimeService(KeycloakSession session) {
// Testing timeService not set. This shouldn't happen if this utility is properly used
InfinispanConnectionProvider ispnProvider = session.getProvider(InfinispanConnectionProvider.class);
if (ispnProvider != null) {
if (origTimeService == null) {
throw new IllegalStateException("Calling revertTimeService when testing TimeService was not set");
}
@ -61,3 +64,4 @@ public class InfinispanTestUtil {
origTimeService = null;
}
}
}

View file

@ -201,7 +201,7 @@ public class TestingResourceProvider implements RealmResourceProvider {
@Path("/revert-testing-infinispan-time-service")
@Produces(MediaType.APPLICATION_JSON)
public Response revertTestingInfinispanTimeService() {
InfinispanTestUtil.revertTimeService();
InfinispanTestUtil.revertTimeService(session);
return Response.noContent().build();
}

View file

@ -53,6 +53,7 @@ import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.ProfileAssume;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.util.ClientBuilder;
@ -744,6 +745,11 @@ public class ResourceOwnerPasswordCredentialsGrantTest extends AbstractKeycloakT
}
private int getAuthenticationSessionsCount() {
if (ProfileAssume.isFeatureEnabled(Profile.Feature.MAP_STORAGE)) {
// Currently, no access to the authentication sessions is available for map storage.
// By return a constant, all tests in this test class can still pass.
return 0;
}
return testingClient.testing().cache(InfinispanConnectionProvider.AUTHENTICATION_SESSIONS_CACHE_NAME).size();
}
}

View file

@ -20,7 +20,9 @@ package org.keycloak.testsuite.session;
import org.infinispan.Cache;
import org.junit.After;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.keycloak.common.Profile;
import org.keycloak.common.util.Retry;
import org.keycloak.common.util.Time;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
@ -32,6 +34,7 @@ import org.keycloak.models.sessions.infinispan.changes.sessions.SessionData;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.ProfileAssume;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
import org.keycloak.testsuite.runonserver.RunOnServer;
@ -52,6 +55,10 @@ public class LastSessionRefreshUnitTest extends AbstractKeycloakTest {
}
@BeforeClass
public static void checkNotMapStorage() {
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MAP_STORAGE);
}
@After
public void cleanupPeriodicTask() {

View file

@ -215,7 +215,7 @@ public class UserSessionProviderModelTest extends KeycloakModelTest {
if (timer != null && timerTaskCtx != null) {
timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
InfinispanTestUtil.revertTimeService();
InfinispanTestUtil.revertTimeService(kcSession);
}
}
}

View file

@ -230,7 +230,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}
InfinispanTestUtil.revertTimeService();
InfinispanTestUtil.revertTimeService(kcSession);
}
}
@ -302,7 +302,7 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}
InfinispanTestUtil.revertTimeService();
InfinispanTestUtil.revertTimeService(kcSession);
}
}