keycloak-uncached
Changes
model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java 15(+1 -14)
Details
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 5015130..c41b3e2 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -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();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java
index 4099d27..a81f30f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java
@@ -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
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
index 265ecd7..d5085b0 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
@@ -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