KEYCLOAK-5245 Restart failures when deleting a client with existing sessions/offline_tokens

This commit is contained in:
mposolda 2017-12-13 13:08:04 +01:00 committed by Marek Posolda
parent 958185ec51
commit 6696c0f0b2
2 changed files with 81 additions and 22 deletions

View file

@ -36,8 +36,10 @@ import javax.persistence.Query;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -162,7 +164,11 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
@Override @Override
public void onClientRemoved(RealmModel realm, ClientModel client) { 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(); num = em.createNamedQuery("deleteDetachedUserSessions").executeUpdate();
} }
@ -223,6 +229,8 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
userSessionIds.add(entity.getUserSessionId()); userSessionIds.add(entity.getUserSessionId());
} }
Set<String> removedClientUUIDs = new HashSet<>();
if (!userSessionIds.isEmpty()) { if (!userSessionIds.isEmpty()) {
TypedQuery<PersistentClientSessionEntity> query2 = em.createNamedQuery("findClientSessionsByUserSessions", PersistentClientSessionEntity.class); TypedQuery<PersistentClientSessionEntity> query2 = em.createNamedQuery("findClientSessionsByUserSessions", PersistentClientSessionEntity.class);
query2.setParameter("userSessionIds", userSessionIds); query2.setParameter("userSessionIds", userSessionIds);
@ -240,7 +248,13 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
PersistentClientSessionEntity clientSession = clientSessions.get(j); PersistentClientSessionEntity clientSession = clientSessions.get(j);
if (clientSession.getUserSessionId().equals(userSession.getId())) { if (clientSession.getUserSessionId().equals(userSession.getId())) {
PersistentAuthenticatedClientSessionAdapter clientSessAdapter = toAdapter(userSession.getRealm(), userSession, clientSession); PersistentAuthenticatedClientSessionAdapter clientSessAdapter = toAdapter(userSession.getRealm(), userSession, clientSession);
// Case when client was removed in the meantime
if (clientSessAdapter.getClient() == null) {
removedClientUUIDs.add(clientSession.getClientId());
} else {
currentClientSessions.put(clientSession.getClientId(), clientSessAdapter); currentClientSessions.put(clientSession.getClientId(), clientSessAdapter);
}
j++; j++;
} else { } else {
next = false; next = false;
@ -249,6 +263,9 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
} }
} }
for (String clientUUID : removedClientUUIDs) {
onClientRemoved(clientUUID);
}
return result; return result;
} }

View file

@ -33,7 +33,6 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider; import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.UserSessionProviderFactory; import org.keycloak.models.UserSessionProviderFactory;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.models.UserManager; import org.keycloak.models.UserManager;
import org.keycloak.services.managers.UserSessionManager; import org.keycloak.services.managers.UserSessionManager;
@ -80,14 +79,72 @@ public class UserSessionInitializerTest {
@Test @Test
public void testUserSessionInitializer() { 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(); 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 started = Time.currentTime();
int serverStartTime = session.getProvider(ClusterProvider.class).getClusterStartupTime(); 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) { for (UserSessionModel origSession : origSessions) {
UserSessionModel userSession = session.sessions().getUserSession(realm, origSession.getId()); UserSessionModel userSession = session.sessions().getUserSession(realm, origSession.getId());
for (AuthenticatedClientSessionModel clientSession : userSession.getAuthenticatedClientSessions().values()) { 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, testApp));
Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(realm, thirdparty)); Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(realm, thirdparty));
// Load sessions from persister into infinispan/memory return origSessions;
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");
} }
private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) { 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); AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
clientSession.setRedirectUri(redirect); clientSession.setRedirectUri(redirect);