KEYCLOAK-5895 CrossDC: NotSerializableException when opening sessions tab in admin console
This commit is contained in:
parent
94ba85c210
commit
6d91ab674b
3 changed files with 97 additions and 19 deletions
|
@ -56,6 +56,7 @@ import org.keycloak.models.sessions.infinispan.util.FuturesHelper;
|
|||
import org.keycloak.models.sessions.infinispan.util.InfinispanUtil;
|
||||
import org.keycloak.models.utils.SessionTimeoutHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
@ -294,11 +295,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
Stream<UserSessionEntity> stream = cache.entrySet().stream()
|
||||
.filter(UserSessionPredicate.create(realm.getId()).client(clientUuid))
|
||||
.map(Mappers.userSessionEntity())
|
||||
// Filter out client sessions that have been invalidated in the meantime
|
||||
.filter(userSession -> {
|
||||
final UUID clientSessionId = userSession.getAuthenticatedClientSessions().get(clientUuid);
|
||||
return clientSessionId != null && clientSessionCacheDecorated.containsKey(clientSessionId);
|
||||
})
|
||||
.sorted(Comparators.userSessionLastSessionRefresh());
|
||||
|
||||
if (firstResult > 0) {
|
||||
|
@ -393,19 +389,10 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
Cache<String, SessionEntityWrapper<UserSessionEntity>> cache = getCache(offline);
|
||||
cache = CacheDecorators.skipCacheLoaders(cache);
|
||||
|
||||
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> clientSessionCache = getClientSessionCache(offline);
|
||||
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> clientSessionCacheDecorated = CacheDecorators.skipCacheLoaders(clientSessionCache);
|
||||
|
||||
final String clientUuid = client.getId();
|
||||
|
||||
return cache.entrySet().stream()
|
||||
.filter(UserSessionPredicate.create(realm.getId()).client(clientUuid))
|
||||
// Filter out client sessions that have been invalidated in the meantime
|
||||
.map(Mappers.userSessionEntity())
|
||||
.filter(userSession -> {
|
||||
final UUID clientSessionId = userSession.getAuthenticatedClientSessions().get(clientUuid);
|
||||
return clientSessionId != null && clientSessionCacheDecorated.containsKey(clientSessionId);
|
||||
})
|
||||
.count();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.keycloak.testsuite.crossdc;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.ws.rs.NotFoundException;
|
||||
|
@ -457,6 +459,75 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLogoutWithAllStartedNodes(
|
||||
@JmxInfinispanCacheStatistics(dc=DC.FIRST, managementPortProperty = "cache.server.management.port", cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
|
||||
@JmxInfinispanCacheStatistics(dc=DC.SECOND, managementPortProperty = "cache.server.2.management.port", cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
|
||||
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
|
||||
|
||||
// Start node2 on every DC
|
||||
startBackendNode(DC.FIRST, 1);
|
||||
startBackendNode(DC.SECOND, 1);
|
||||
|
||||
// Create sessions. Don't include remote stats. Size is smaller because of distributed cache
|
||||
List<OAuthClient.AccessTokenResponse> responses = createInitialSessions(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME,
|
||||
false, cacheDc1Statistics, cacheDc2Statistics, false);
|
||||
|
||||
// Simulate displaying sessions in admin console
|
||||
Retry.execute(() -> {
|
||||
assertTestAppActiveSessionsCount(SESSIONS_COUNT);
|
||||
}, 50, 50);
|
||||
|
||||
|
||||
// Logout realm and check sessions not anymore in admin console
|
||||
getAdminClient().realm(REALM_NAME).logoutAll();
|
||||
|
||||
Retry.execute(() -> {
|
||||
assertTestAppActiveSessionsCount(0);
|
||||
}, 50, 50);
|
||||
|
||||
|
||||
// Login again and check sessions back in
|
||||
responses = createInitialSessions(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME,
|
||||
false, cacheDc1Statistics, cacheDc2Statistics, false);
|
||||
|
||||
Retry.execute(() -> {
|
||||
assertTestAppActiveSessionsCount(SESSIONS_COUNT);
|
||||
}, 50, 50);
|
||||
|
||||
|
||||
// Logout user and check sessions not anymore in admin console
|
||||
ApiUtil.findUserByUsernameId(getAdminClient().realm(REALM_NAME), "login-test").logout();
|
||||
|
||||
Retry.execute(() -> {
|
||||
assertTestAppActiveSessionsCount(0);
|
||||
}, 50, 50);
|
||||
|
||||
// Stop both nodes
|
||||
stopBackendNode(DC.FIRST, 1);
|
||||
stopBackendNode(DC.SECOND, 1);
|
||||
}
|
||||
|
||||
private void assertTestAppActiveSessionsCount(int expectedSessionsCount) {
|
||||
List<Map<String, String>> sessions = getAdminClient().realm(REALM_NAME).getClientSessionStats();
|
||||
|
||||
Optional<Map<String, String>> optional = sessions.stream().filter((Map<String, String> map) -> {
|
||||
return map.get("clientId").equals("test-app");
|
||||
}).findFirst();
|
||||
|
||||
if (expectedSessionsCount == 0) {
|
||||
// No sessions present. Statistics for the client not included
|
||||
Assert.assertFalse(optional.isPresent());
|
||||
} else {
|
||||
Map<String, String> testAppSessions = optional.get();
|
||||
Assert.assertEquals(expectedSessionsCount, Integer.parseInt(testAppSessions.get("active")));
|
||||
}
|
||||
|
||||
List<UserSessionRepresentation> userSessions = ApiUtil.findClientByClientId(getAdminClient().realm(REALM_NAME), "test-app").getUserSessions(0, 100);
|
||||
Assert.assertEquals(expectedSessionsCount, userSessions.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
// AUTH SESSIONS
|
||||
|
||||
|
|
|
@ -140,14 +140,13 @@ public class UserSessionProviderOfflineTest {
|
|||
// Assert userSession revoked
|
||||
testApp = realm.getClientByClientId("test-app");
|
||||
thirdparty = realm.getClientByClientId("third-party");
|
||||
Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
|
||||
// Still 2 sessions. The count of sessions by client may not be accurate after revoke due the
|
||||
// performance optimizations (the "127.0.0.1" session still has another client "thirdparty" in it)
|
||||
Assert.assertEquals(2, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, thirdparty));
|
||||
|
||||
List<UserSessionModel> testAppSessions = session.sessions().getOfflineUserSessions(realm, testApp, 0, 10);
|
||||
List<UserSessionModel> thirdpartySessions = session.sessions().getOfflineUserSessions(realm, thirdparty, 0, 10);
|
||||
Assert.assertEquals(1, testAppSessions.size());
|
||||
Assert.assertEquals("127.0.0.3", testAppSessions.get(0).getIpAddress());
|
||||
Assert.assertEquals("user2", testAppSessions.get(0).getUser().getUsername());
|
||||
Assert.assertEquals(1, thirdpartySessions.size());
|
||||
Assert.assertEquals("127.0.0.1", thirdpartySessions.get(0).getIpAddress());
|
||||
Assert.assertEquals("user1", thirdpartySessions.get(0).getUser().getUsername());
|
||||
|
@ -160,6 +159,27 @@ public class UserSessionProviderOfflineTest {
|
|||
clients = sessionManager.findClientsWithOfflineToken(realm, user2);
|
||||
Assert.assertEquals(1, clients.size());
|
||||
Assert.assertEquals("test-app", clients.iterator().next().getClientId());
|
||||
|
||||
// Revoke the second session for user1 too.
|
||||
sessionManager.revokeOfflineToken(user1, thirdparty);
|
||||
|
||||
resetSession();
|
||||
|
||||
testApp = realm.getClientByClientId("test-app");
|
||||
thirdparty = realm.getClientByClientId("third-party");
|
||||
|
||||
// Accurate count now. All sessions of user1 cleared
|
||||
Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(realm, thirdparty));
|
||||
|
||||
List<UserSessionModel> testAppSessions = session.sessions().getOfflineUserSessions(realm, testApp, 0, 10);
|
||||
|
||||
Assert.assertEquals(1, testAppSessions.size());
|
||||
Assert.assertEquals("127.0.0.3", testAppSessions.get(0).getIpAddress());
|
||||
Assert.assertEquals("user2", testAppSessions.get(0).getUser().getUsername());
|
||||
|
||||
clients = sessionManager.findClientsWithOfflineToken(realm, user1);
|
||||
Assert.assertEquals(0, clients.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in a new issue