Fix unexpected expiration when import offline client session

Closes #23397
This commit is contained in:
Lex Cao 2023-09-21 02:40:55 +08:00 committed by Alexander Schwartz
parent 31759f9c37
commit eedc4ceb18
2 changed files with 60 additions and 0 deletions

View file

@ -1063,6 +1063,9 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
AuthenticatedClientSessionEntity entity = createAuthenticatedClientSessionInstance(clientSession, AuthenticatedClientSessionEntity entity = createAuthenticatedClientSessionInstance(clientSession,
sessionToImportInto.getRealm().getId(), clientSession.getClient().getId(), offline); sessionToImportInto.getRealm().getId(), clientSession.getClient().getId(), offline);
// Update timestamp to same value as userSession. LastSessionRefresh of userSession from DB will have correct value
entity.setTimestamp(sessionToImportInto.getLastSessionRefresh());
if (checkExpiration) { if (checkExpiration) {
SessionFunction<AuthenticatedClientSessionEntity> lifespanChecker = offline SessionFunction<AuthenticatedClientSessionEntity> lifespanChecker = offline
? SessionTimeouts::getOfflineClientSessionLifespanMs : SessionTimeouts::getClientSessionLifespanMs; ? SessionTimeouts::getOfflineClientSessionLifespanMs : SessionTimeouts::getClientSessionLifespanMs;

View file

@ -49,8 +49,10 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -413,6 +415,61 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
}); });
} }
@Test
public void testLoadingOfflineClientSessionWhenCreatedBeforeSessionTime() {
// setup idle timeout for the realm
int idleTimeout = (int) TimeUnit.DAYS.toSeconds(1);
withRealm(realmId, (session, realmModel) -> {
realmModel.setClientOfflineSessionIdleTimeout(idleTimeout);
return null;
});
// create online user and client sessions
inComittedTransaction((Consumer<KeycloakSession>) session -> UserSessionPersisterProviderTest.createSessions(session, realmId));
// create offline user and client sessions
List<String> offlineUserSessionIds = withRealm(realmId, (session, realm) -> session.sessions()
.getUserSessionsStream(realm, realm.getClientByClientId("test-app"))
.map(userSession -> {
UserSessionModel offlineUserSession = Optional.ofNullable(
session.sessions().getOfflineUserSession(realm, userSession.getId())
).orElseGet(() -> session.sessions().createOfflineUserSession(userSession));
userSession.getAuthenticatedClientSessions()
.values()
.forEach(clientSession -> {
// set timestamp manually to make sure the client session is created before session time
// this simulates the cases when the offline client sessions are created before the session time
clientSession.setTimestamp(Time.currentTime() - idleTimeout * 2);
session.sessions().createOfflineClientSession(clientSession, offlineUserSession);
});
return offlineUserSession.getId();
}
).collect(Collectors.toList())
);
withRealm(realmId, (session, realm) -> {
// remove offline client sessions from the cache
// this simulates the cases when offline client sessions are lost from the cache due to various reasons (a cache limit/expiration/preloading issue)
session.getProvider(InfinispanConnectionProvider.class)
.getCache(InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME).clear();
String clientUUID = realm.getClientByClientId("test-app").getId();
offlineUserSessionIds.forEach(id -> {
UserSessionModel offlineUserSession = session.sessions().getOfflineUserSession(realm, id);
// each associated offline client session should be found by looking into persister
AuthenticatedClientSessionModel offlineClientSession = offlineUserSession.getAuthenticatedClientSessionByClient(clientUUID);
Assert.assertNotNull(offlineClientSession);
Assert.assertEquals(offlineUserSession.getLastSessionRefresh(), offlineClientSession.getTimestamp());
});
return null;
});
}
private static Set<String> createOfflineSessionIncludeClientSessions(KeycloakSession session, UserSessionModel private static Set<String> createOfflineSessionIncludeClientSessions(KeycloakSession session, UserSessionModel
userSession) { userSession) {
Set<String> offlineSessions = new HashSet<>(); Set<String> offlineSessions = new HashSet<>();