Remove offline sessions when deleting a realm
This commit is contained in:
parent
b563028f42
commit
c8a6846ee0
3 changed files with 54 additions and 17 deletions
|
@ -34,7 +34,6 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||||
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
|
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -351,8 +350,6 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
||||||
@Override
|
@Override
|
||||||
public void onRealmRemoved(RealmModel realm) {
|
public void onRealmRemoved(RealmModel realm) {
|
||||||
LOG.tracef("onRealmRemoved(%s)%s", realm, getShortStackTrace());
|
LOG.tracef("onRealmRemoved(%s)%s", realm, getShortStackTrace());
|
||||||
|
|
||||||
removeUserSessions(realm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -526,6 +523,19 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all online and offline user sessions that belong to the provided {@link RealmModel}.
|
||||||
|
* @param realm
|
||||||
|
*/
|
||||||
|
protected void removeAllUserSessions(RealmModel realm) {
|
||||||
|
DefaultModelCriteria<UserSessionModel> mcb = criteria();
|
||||||
|
mcb = mcb.compare(UserSessionModel.SearchableFields.REALM_ID, Operator.EQ, realm.getId());
|
||||||
|
|
||||||
|
LOG.tracef("removeAllUserSessions(%s)%s", realm, getShortStackTrace());
|
||||||
|
|
||||||
|
userSessionTx.delete(withCriteria(mcb));
|
||||||
|
}
|
||||||
|
|
||||||
private Stream<MapUserSessionEntity> getOfflineUserSessionEntityStream(RealmModel realm, String userSessionId) {
|
private Stream<MapUserSessionEntity> getOfflineUserSessionEntityStream(RealmModel realm, String userSessionId) {
|
||||||
if (userSessionId == null) {
|
if (userSessionId == null) {
|
||||||
return Stream.empty();
|
return Stream.empty();
|
||||||
|
|
|
@ -16,30 +16,17 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.models.map.userSession;
|
package org.keycloak.models.map.userSession;
|
||||||
|
|
||||||
import org.keycloak.Config.Scope;
|
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.component.AmphibianProviderFactory;
|
|
||||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
|
||||||
import org.keycloak.models.ClientModel;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.UserSessionProvider;
|
|
||||||
import org.keycloak.models.UserSessionProviderFactory;
|
import org.keycloak.models.UserSessionProviderFactory;
|
||||||
import org.keycloak.models.map.client.MapClientProvider;
|
|
||||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||||
import org.keycloak.models.map.storage.MapStorage;
|
|
||||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
|
||||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
|
||||||
import org.keycloak.models.map.storage.MapStorageSpi;
|
|
||||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
|
||||||
import org.keycloak.provider.InvalidationHandler;
|
import org.keycloak.provider.InvalidationHandler;
|
||||||
|
|
||||||
|
import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.REALM_BEFORE_REMOVE;
|
||||||
import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.USER_BEFORE_REMOVE;
|
import static org.keycloak.models.map.common.AbstractMapProviderFactory.MapProviderObjectType.USER_BEFORE_REMOVE;
|
||||||
import static org.keycloak.models.map.common.AbstractMapProviderFactory.uniqueCounter;
|
|
||||||
import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||||
|
@ -64,6 +51,8 @@ public class MapUserSessionProviderFactory extends AbstractMapProviderFactory<Ma
|
||||||
public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... params) {
|
public void invalidate(KeycloakSession session, InvalidableObjectType type, Object... params) {
|
||||||
if (type == USER_BEFORE_REMOVE) {
|
if (type == USER_BEFORE_REMOVE) {
|
||||||
create(session).removeUserSessions((RealmModel) params[0], (UserModel) params[1]);
|
create(session).removeUserSessions((RealmModel) params[0], (UserModel) params[1]);
|
||||||
|
} else if (type == REALM_BEFORE_REMOVE) {
|
||||||
|
create(session).removeAllUserSessions((RealmModel) params[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.testsuite.model.session;
|
package org.keycloak.testsuite.model.session;
|
||||||
|
|
||||||
|
import org.infinispan.client.hotrod.RemoteCache;
|
||||||
import org.infinispan.commons.CacheException;
|
import org.infinispan.commons.CacheException;
|
||||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
@ -27,6 +28,10 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.UserSessionProvider;
|
import org.keycloak.models.UserSessionProvider;
|
||||||
|
import org.keycloak.models.map.storage.ModelEntityUtil;
|
||||||
|
import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory;
|
||||||
|
import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionProvider;
|
||||||
|
import org.keycloak.models.map.storage.hotRod.userSession.HotRodUserSessionEntity;
|
||||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||||
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProvider;
|
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProvider;
|
||||||
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProviderFactory;
|
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProviderFactory;
|
||||||
|
@ -123,6 +128,39 @@ public class OfflineSessionPersistenceTest extends KeycloakModelTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequireProvider(value = HotRodConnectionProvider.class, only = DefaultHotRodConnectionProviderFactory.PROVIDER_ID)
|
||||||
|
public void testOfflineSessionsRemovedAfterDeleteRealm() {
|
||||||
|
String realmId2 = inComittedTransaction(session -> { return prepareRealm(session, "realm2").getId(); });
|
||||||
|
List<String> userIds2 = withRealm(realmId2, (session, realm) -> IntStream.range(0, USER_COUNT)
|
||||||
|
.mapToObj(i -> session.users().addUser(realm, "user2-" + i))
|
||||||
|
.map(UserModel::getId)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<String> offlineSessionIds2 = createOfflineSessions(realmId2, userIds2);
|
||||||
|
assertOfflineSessionsExist(realmId2, offlineSessionIds2);
|
||||||
|
|
||||||
|
// Simulate server restart
|
||||||
|
reinitializeKeycloakSessionFactory();
|
||||||
|
|
||||||
|
assertOfflineSessionsExist(realmId2, offlineSessionIds2);
|
||||||
|
|
||||||
|
inComittedTransaction(session -> {
|
||||||
|
session.realms().removeRealm(realmId2);
|
||||||
|
});
|
||||||
|
|
||||||
|
inComittedTransaction(session -> {
|
||||||
|
HotRodConnectionProvider provider = session.getProvider(HotRodConnectionProvider.class);
|
||||||
|
RemoteCache<String, HotRodUserSessionEntity> remoteCache = provider.getRemoteCache(ModelEntityUtil.getModelName(UserSessionModel.class));
|
||||||
|
assertThat(remoteCache, Matchers.anEmptyMap());
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
withRealm(realmId2, (session, realm) -> realm == null ? false : new RealmManager(session).removeRealm(realm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPersistenceSingleNode() {
|
public void testPersistenceSingleNode() {
|
||||||
List<String> offlineSessionIds = createOfflineSessions(realmId, userIds);
|
List<String> offlineSessionIds = createOfflineSessions(realmId, userIds);
|
||||||
|
|
Loading…
Reference in a new issue