keycloak-uncached

KEYCLOAK-5245 Restart failures when deleting a client with

12/13/2017 10:08:04 AM

Details

diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/session/JpaUserSessionPersisterProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/session/JpaUserSessionPersisterProvider.java
index 8d07942..a4c02de 100644
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/session/JpaUserSessionPersisterProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/session/JpaUserSessionPersisterProvider.java
@@ -36,8 +36,10 @@ import javax.persistence.Query;
 import javax.persistence.TypedQuery;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -162,7 +164,11 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
 
     @Override
     public void onClientRemoved(RealmModel realm, ClientModel client) {
-        int num = em.createNamedQuery("deleteClientSessionsByClient").setParameter("clientId", client.getId()).executeUpdate();
+        onClientRemoved(client.getId());
+    }
+
+    private void onClientRemoved(String clientUUID) {
+        int num = em.createNamedQuery("deleteClientSessionsByClient").setParameter("clientId", clientUUID).executeUpdate();
         num = em.createNamedQuery("deleteDetachedUserSessions").executeUpdate();
     }
 
@@ -223,6 +229,8 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
             userSessionIds.add(entity.getUserSessionId());
         }
 
+        Set<String> removedClientUUIDs = new HashSet<>();
+
         if (!userSessionIds.isEmpty()) {
             TypedQuery<PersistentClientSessionEntity> query2 = em.createNamedQuery("findClientSessionsByUserSessions", PersistentClientSessionEntity.class);
             query2.setParameter("userSessionIds", userSessionIds);
@@ -240,7 +248,13 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
                     PersistentClientSessionEntity clientSession = clientSessions.get(j);
                     if (clientSession.getUserSessionId().equals(userSession.getId())) {
                         PersistentAuthenticatedClientSessionAdapter clientSessAdapter = toAdapter(userSession.getRealm(), userSession, clientSession);
-                        currentClientSessions.put(clientSession.getClientId(), clientSessAdapter);
+
+                        // Case when client was removed in the meantime
+                        if (clientSessAdapter.getClient() == null) {
+                            removedClientUUIDs.add(clientSession.getClientId());
+                        } else {
+                            currentClientSessions.put(clientSession.getClientId(), clientSessAdapter);
+                        }
                         j++;
                     } else {
                         next = false;
@@ -249,6 +263,9 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
             }
         }
 
+        for (String clientUUID : removedClientUUIDs) {
+            onClientRemoved(clientUUID);
+        }
 
         return result;
     }
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
index c8ba747..9574052 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
@@ -33,7 +33,6 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.UserSessionProvider;
 import org.keycloak.models.UserSessionProviderFactory;
-import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.models.UserManager;
 import org.keycloak.services.managers.UserSessionManager;
@@ -80,14 +79,72 @@ public class UserSessionInitializerTest {
 
     @Test
     public void testUserSessionInitializer() {
-        UserSessionModel[] origSessions = createSessions();
+        int started = Time.currentTime();
+        int serverStartTime = session.getProvider(ClusterProvider.class).getClusterStartupTime();
+
+        UserSessionModel[] origSessions = createSessionsInPersisterOnly();
+
+        // Load sessions from persister into infinispan/memory
+        UserSessionProviderFactory userSessionFactory = (UserSessionProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserSessionProvider.class);
+        userSessionFactory.loadPersistentSessions(session.getKeycloakSessionFactory(), 1, 2);
 
         resetSession();
 
-        // Create and persist offline sessions
+        // Assert sessions are in
+        ClientModel testApp = realm.getClientByClientId("test-app");
+        ClientModel thirdparty = realm.getClientByClientId("third-party");
+        Assert.assertEquals(3, session.sessions().getOfflineSessionsCount(realm, testApp));
+        Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, thirdparty));
+
+        List<UserSessionModel> loadedSessions = session.sessions().getOfflineUserSessions(realm, testApp, 0, 10);
+        UserSessionProviderTest.assertSessions(loadedSessions, origSessions);
+
+        UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[0].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, serverStartTime, "test-app", "third-party");
+        UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[1].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.2", started, serverStartTime, "test-app");
+        UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[2].getId(), session.users().getUserByUsername("user2", realm), "127.0.0.3", started, serverStartTime, "test-app");
+    }
+
+
+    // KEYCLOAK-5245
+    @Test
+    public void testUserSessionInitializerWithDeletingClient() {
         int started = Time.currentTime();
         int serverStartTime = session.getProvider(ClusterProvider.class).getClusterStartupTime();
 
+        UserSessionModel[] origSessions = createSessionsInPersisterOnly();
+
+        // Delete one of the clients now. Delete it directly in DB just for the purpose of simulating the issue (normally clients should be removed through ClientManager)
+        ClientModel testApp = realm.getClientByClientId("test-app");
+        realm.removeClient(testApp.getId());
+
+        resetSession();
+
+        // Load sessions from persister into infinispan/memory
+        UserSessionProviderFactory userSessionFactory = (UserSessionProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserSessionProvider.class);
+        userSessionFactory.loadPersistentSessions(session.getKeycloakSessionFactory(), 1, 2);
+
+        resetSession();
+
+        // Assert sessions are in
+        ClientModel thirdparty = realm.getClientByClientId("third-party");
+        Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, thirdparty));
+
+        List<UserSessionModel> loadedSessions = session.sessions().getOfflineUserSessions(realm, thirdparty, 0, 10);
+        Assert.assertEquals(1, loadedSessions.size());
+
+        UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[0].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, serverStartTime, "third-party");
+
+        // Revert client
+        realm.addClient("test-app");
+    }
+
+
+    // Create sessions in persister+infinispan, but then delete them from infinispan cache. This is to allow later testing of initializer. Return the list of "origSessions"
+    private UserSessionModel[] createSessionsInPersisterOnly() {
+        UserSessionModel[] origSessions = createSessions();
+
+        resetSession();
+
         for (UserSessionModel origSession : origSessions) {
             UserSessionModel userSession = session.sessions().getUserSession(realm, origSession.getId());
             for (AuthenticatedClientSessionModel clientSession : userSession.getAuthenticatedClientSessions().values()) {
@@ -111,25 +168,10 @@ public class UserSessionInitializerTest {
         Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(realm, testApp));
         Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(realm, thirdparty));
 
-        // Load sessions from persister into infinispan/memory
-        UserSessionProviderFactory userSessionFactory = (UserSessionProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserSessionProvider.class);
-        userSessionFactory.loadPersistentSessions(session.getKeycloakSessionFactory(), 1, 2);
-
-        resetSession();
-
-        // Assert sessions are in
-        testApp = realm.getClientByClientId("test-app");
-        Assert.assertEquals(3, session.sessions().getOfflineSessionsCount(realm, testApp));
-        Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, thirdparty));
-
-        List<UserSessionModel> loadedSessions = session.sessions().getOfflineUserSessions(realm, testApp, 0, 10);
-        UserSessionProviderTest.assertSessions(loadedSessions, origSessions);
-
-        UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[0].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, serverStartTime, "test-app", "third-party");
-        UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[1].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.2", started, serverStartTime, "test-app");
-        UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[2].getId(), session.users().getUserByUsername("user2", realm), "127.0.0.3", started, serverStartTime, "test-app");
+        return origSessions;
     }
 
+
     private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
         AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
         clientSession.setRedirectUri(redirect);