keycloak-uncached

KEYCLOAK-5895 CrossDC: NotSerializableException when opening

11/23/2017 1:38:24 PM

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