8000 Add support for overriding offline session lifespan values by thomasdarimont · Pull Request #24302 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
< 10000 td id="diff-1445188baf4e2f8d90de96fc76d0423dd86425d2a0a9ede5c3e20d88c3f85077L973" data-line-number="973" class="blob-num blob-num-context js-linkable-line-number">
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {

protected final boolean loadOfflineSessionsFromDatabase;

protected final SessionFunction offlineSessionCacheEntryLifespanAdjuster;

protected final SessionFunction offlineClientSessionCacheEntryLifespanAdjuster;

public InfinispanUserSessionProvider(KeycloakSession session,
RemoteCacheInvoker remoteCacheInvoker,
CrossDCLastSessionRefreshStore lastSessionRefreshStore,
Expand All @@ -128,7 +132,9 @@ public InfinispanUserSessionProvider(KeycloakSession session,
Cache<String, SessionEntityWrapper<UserSessionEntity>> offlineSessionCache,
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> clientSessionCache,
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> offlineClientSessionCache,
boolean loadOfflineSessionsFromDatabase) {
boolean loadOfflineSessionsFromDatabase,
SessionFunction<UserSessionEntity> offlineSessionCacheEntryLifespanAdjuster,
SessionFunction<AuthenticatedClientSessionEntity> offlineClientSessionCacheEntryLifespanAdjuster) {
this.session = session;

this.sessionCache = sessionCache;
Expand All @@ -137,9 +143,9 @@ public InfinispanUserSessionProvider(KeycloakSession session,
this.offlineClientSessionCache = offlineClientSessionCache;

this.sessionTx = new InfinispanChangelogBasedTransaction<>(session, sessionCache, remoteCacheInvoker, SessionTimeouts::getUserSessionLifespanMs, SessionTimeouts::getUserSessionMaxIdleMs);
this.offlineSessionTx = new InfinispanChangelogBasedTransaction<>(session, offlineSessionCache, remoteCacheInvoker, SessionTimeouts::getOfflineSessionLifespanMs, SessionTimeouts::getOfflineSessionMaxIdleMs);
this.offlineSessionTx = new InfinispanChangelogBasedTransaction<>(session, offlineSessionCache, remoteCacheInvoker, offlineSessionCacheEntryLifespanAdjuster, SessionTimeouts::getOfflineSessionMaxIdleMs);
this.clientSessionTx = new InfinispanChangelogBasedTransaction<>(session, clientSessionCache, remoteCacheInvoker, SessionTimeouts::getClientSessionLifespanMs, SessionTimeouts::getClientSessionMaxIdleMs);
this.offlineClientSessionTx = new InfinispanChangelogBasedTransaction<>(session, offlineClientSessionCache, remoteCacheInvoker, SessionTimeouts::getOfflineClientSessionLifespanMs, SessionTimeouts::getOfflineClientSessionMaxIdleMs);
this.offlineClientSessionTx = new InfinispanChangelogBasedTransaction<>(session, offlineClientSessionCache, remoteCacheInvoker, offlineClientSessionCacheEntryLifespanAdjuster, SessionTimeouts::getOfflineClientSessionMaxIdleMs);

this.clusterEventsSenderTx = new SessionEventsSenderTransaction(session);

Expand All @@ -149,6 +155,8 @@ public InfinispanUserSessionProvider(KeycloakSession session,
this.remoteCacheInvoker = remoteCacheInvoker;
this.keyGenerator = keyGenerator;
this.loadOfflineSessionsFromDatabase = loadOfflineSessionsFromDatabase;
this.offlineSessionCacheEntryLifespanAdjuster = offlineSessionCacheEntryLifespanAdjuster;
this.offlineClientSessionCacheEntryLifespanAdjuster = offlineClientSessionCacheEntryLifespanAdjuster;

session.getTransactionManager().enlistAfterCompletion(clusterEventsSenderTx);
session.getTransactionManager().enlistAfterCompletion(sessionTx);
Expand Down Expand Up @@ -911,7 +919,7 @@ public void importUserSessions(Collection<UserSessionModel> persistentUserSessio
boolean importWithExpiration = sessionsById.size() == 1;
if (importWithExpiration) {
importSessionsWithExpiration(sessionsById, cache,
offline ? SessionTimeouts::getOfflineSessionLifespanMs : SessionTimeouts::getUserSessionLifespanMs,
offline ? offlineSessionCacheEntryLifespanAdjuster : SessionTimeouts::getUserSessionLifespanMs,
offline ? SessionTimeouts::getOfflineSessionMaxIdleMs : SessionTimeouts::getUserSessionMaxIdleMs);
} else {
Retry.executeWithBackoff((int iteration) -> {
Expand All @@ -928,7 +936,7 @@ public void importUserSessions(Collection<UserSessionModel> persistentUserSessio

if (importWithExpiration) {
importSessionsWithExpiration(sessionsByIdForTransport, remoteCache,
offline ? SessionTimeouts::getOfflineSessionLifespanMs : SessionTimeouts::getUserSessionLifespanMs,
offline ? offlineSessionCacheEntryLifespanAdjuster : SessionTimeouts::getUserSessionLifespanMs,
offline ? SessionTimeouts::getOfflineSessionMaxIdleMs : SessionTimeouts::getUserSessionMaxIdleMs);
} else {
Retry.executeWithBackoff((int iteration) -> {
Expand All @@ -955,7 +963,7 @@ public void importUserSessions(Collection<UserSessionModel> persistentUserSessio

if (importWithExpiration) {
importSessionsWithExpiration(clientSessionsById, clientSessCache,
offline ? SessionTimeouts::getOfflineClientSessionLifespanMs : SessionTimeouts::getClientSessionLifespanMs,
offline ? offlineClientSessionCacheEntryLifespanAdjuster : SessionTimeouts::getClientSessionLifespanMs,
offline ? SessionTimeouts::getOfflineClientSessionMaxIdleMs : SessionTimeouts::getClientSessionMaxIdleMs);
} else {
Retry.executeWithBackoff((int iteration) -> {
Expand All @@ -972,7 +980,7 @@ public void importUserSessions(Collection<UserSessionModel> persistentUserSessio

if (importWithExpiration) {
importSessionsWithExpiration(sessionsByIdForTransport, remoteCacheClientSessions,
offline ? SessionTimeouts::getOfflineClientSessionLifespanMs : SessionTimeouts::getClientSessionLifespanMs,
offline ? offlineClientSessionCacheEntryLifespanAdjuster : SessionTimeouts::getClientSessionLifespanMs,
offline ? SessionTimeouts::getOfflineClientSessionMaxIdleMs : SessionTimeouts::getClientSessionMaxIdleMs);
} else {
Retry.executeWithBackoff((int iteration) -> {
Expand Down Expand Up @@ -1090,7 +1098,7 @@ private AuthenticatedClientSessionAdapter importClientSession(UserSessionAdapter

if (checkExpiration) {
SessionFunction<AuthenticatedClientSessionEntity> lifespanChecker = offline
? SessionTimeouts::getOfflineClientSessionLifespanMs : SessionTimeouts::getClientSessionLifespanMs;
? offlineClientSessionCacheEntryLifespanAdjuster : SessionTimeouts::getClientSessionLifespanMs;
SessionFunction<AuthenticatedClientSessionEntity> idleTimeoutChecker = offline
? SessionTimeouts::getOfflineClientSessionMaxIdleMs : SessionTimeouts::getClientSessionMaxIdleMs;
if (idleTimeoutChecker.apply(sessionToImportInto.getRealm(), clientSession.getClient(), entity) == SessionTimeouts.ENTRY_EXPIRED_FLAG
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,16 @@
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderEventListener;
import org.keycloak.provider.ServerInfoAwareProviderFactory;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;

public class InfinispanUserSessionProviderFactory implements UserSessionProviderFactory, EnvironmentDependentProviderFactory {
public class InfinispanUserSessionProviderFactory implements UserSessionProviderFactory, EnvironmentDependentProviderFactory, ServerInfoAwareProviderFactory {

private static final Logger log = Logger.getLogger(InfinispanUserSessionProviderFactory.class);

Expand All @@ -83,6 +86,10 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider

private boolean preloadOfflineSessionsFromDatabase;

private long offlineSessionCacheEntryLifespanOverride;

private long offlineClientSessionCacheEntryLifespanOverride;

private Config.Scope config;

private RemoteCacheInvoker remoteCacheInvoker;
Expand All @@ -99,14 +106,29 @@ public InfinispanUserSessionProvider create(KeycloakSession session) {
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> clientSessionCache = connections.getCache(InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME);
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> offlineClientSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME);

return new InfinispanUserSessionProvider(session, remoteCacheInvoker, lastSessionRefreshStore, offlineLastSessionRefreshStore,
persisterLastSessionRefreshStore, keyGenerator, cache, offlineSessionsCache, clientSessionCache, offlineClientSessionsCache, !preloadOfflineSessionsFromDatabase);
return new InfinispanUserSessionProvider( //
session, //
remoteCacheInvoker, //
lastSessionRefreshStore, //
offlineLastSessionRefreshStore, //
persisterLastSessionRefreshStore, //
keyGenerator, //
cache, //
offlineSessionsCache, //
clientSessionCache, //
offlineClientSessionsCache, //
!preloadOfflineSessionsFromDatabase, //
this::deriveOfflineSessionCacheEntryLifespanMs, //
this::deriveOfflineClientSessionCacheEntryLifespanOverrideMs //
);
}

@Override
public void init(Config.Scope config) {
this.config = config;
preloadOfflineSessionsFromDatabase = config.getBoolean("preloadOfflineSessionsFromDatabase", false);
offlineSessionCacheEntryLifespanOverride = config.getInt("offlineSessionCacheEntryLifespanOverride", -1);
offlineClientSessionCacheEntryLifespanOverride = config.getInt("offlineClientSessionCacheEntryLifespanOverride", -1);
}

@Override
Expand Down Expand Up @@ -279,7 +301,7 @@ protected void checkRemoteCaches(KeycloakSession session) {
Cache<String, SessionEntityWrapper<UserSessionEntity>> offlineSessionsCache = ispn.getCache(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME);
RemoteCache offlineSessionsRemoteCache = checkRemoteCache(session, offlineSessionsCache, (RealmModel realm) -> {
return Time.toMillis(realm.getOfflineSessionIdleTimeout());
}, SessionTimeouts::getOfflineSessionLifespanMs, SessionTimeouts::getOfflineSessionMaxIdleMs);
}, this::deriveOfflineSessionCacheEntryLifespanMs, SessionTimeouts::getOfflineSessionMaxIdleMs);

if (offlineSessionsRemoteCache != null) {
offlineLastSessionRefreshStore = new CrossDCLastSessionRefreshStoreFactory().createAndInit(session, offlineSessionsCache, true);
Expand All @@ -288,7 +310,7 @@ protected void checkRemoteCaches(KeycloakSession session) {
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> offlineClientSessionsCache = ispn.getCache(InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME);
checkRemoteCache(session, offlineClientSessionsCache, (RealmModel realm) -> {
return Time.toMillis(realm.getOfflineSessionIdleTimeout());
}, SessionTimeouts::getOfflineClientSessionLifespanMs, SessionTimeouts::getOfflineClientSessionMaxIdleMs);
}, this::deriveOfflineClientSessionCacheEntryLifespanOverrideMs, SessionTimeouts::getOfflineClientSessionMaxIdleMs);
}

private <K, V extends SessionEntity> RemoteCache checkRemoteCache(KeycloakSession session, Cache<K, SessionEntityWrapper<V>> ispnCache, RemoteCacheInvoker.MaxIdleTimeLoader maxIdleLoader,
Expand All @@ -315,6 +337,42 @@ private <K, V extends SessionEntity> RemoteCache checkRemoteCache(KeycloakSessio
}
}

protected Long deriveOfflineSessionCacheEntryLifespanMs(RealmModel realm, ClientModel client, UserSessionEntity entity) {

long configuredOfflineSessionLifespan = SessionTimeouts.getOfflineSessionLifespanMs(realm, client, entity);

if (offlineSessionCacheEntryLifespanOverride == -1) {
// override not configured -> take the value from realm settings
return configuredOfflineSessionLifespan;
}

if (configuredOfflineSessionLifespan == -1) {
// "Offline Session Max Limited" is "off"
return offlineSessionCacheEntryLifespanOverride;
}

// both values are configured, Offline Session Max could be smaller than the override, so we use the minimum of both
return Math.min(configuredOfflineSessionLifespan, offlineSessionCacheEntryLifespanOverride);
}

protected Long deriveOfflineClientSessionCacheEntryLifespanOverrideMs(RealmModel realm, ClientModel client, AuthenticatedClientSessionEntity entity) {

long configuredOfflineClientSessionLifespan = SessionTimeouts.getOfflineClientSessionLifespanMs(realm, client, entity);

if (offlineClientSessionCacheEntryLifespanOverride == -1) {
// override not configured -> take the value from realm settings
return configuredOfflineClientSessionLifespan;
}

if (configuredOfflineClientSessionLifespan == -1) {
// "Offline Session Max Limited" is "off"
return offlineClientSessionCacheEntryLifespanOverride;
}

// both values are configured, Offline Session Max could be smaller than the override, so we use the minimum of both
return Math.min(offlineClientSessionCacheEntryLifespanOverride, configuredOfflineClientSessionLifespan);
}


private void loadSessionsFromRemoteCaches(KeycloakSession session) {
for (String cacheName : remoteCacheInvoker.getRemoteCacheNames()) {
Expand Down Expand Up @@ -366,5 +424,14 @@ public int order() {
public boolean isSupported() {
return !Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
}

@Override
public Map<String, String> getOperationalInfo() {
Map<String, String> info = new HashMap<>();
info.put("preloadOfflineSessionsFromDatabase", Boolean.toString(preloadOfflineSessionsFromDatabase));
info.put("offlineSessionCacheEntryLifespanOverride", Long.toString(offlineSessionCacheEntryLifespanOverride));
info.put("offlineClientSessionCacheEntryLifespanOverride", Long.toString(offlineClientSessionCacheEntryLifespanOverride));
return info;
}
}

0