Merge pull request #4384 from mposolda/ispn-clientListeners-bugs
KEYCLOAK-4187 SessionExpirationCrossDCTest - added tests for user log…
This commit is contained in:
commit
a80808c5c9
6 changed files with 97 additions and 13 deletions
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.models.sessions.infinispan.changes;
|
package org.keycloak.models.sessions.infinispan.changes;
|
||||||
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -244,6 +244,10 @@ public class ConcurrencyJDGSessionsCacheTest {
|
||||||
SessionEntity session = (SessionEntity) remoteCache.get(cacheKey);
|
SessionEntity session = (SessionEntity) remoteCache.get(cacheKey);
|
||||||
SessionEntityWrapper sessionWrapper = new SessionEntityWrapper(session);
|
SessionEntityWrapper sessionWrapper = new SessionEntityWrapper(session);
|
||||||
|
|
||||||
|
if (listenerCount.get() % 100 == 0) {
|
||||||
|
logger.infof("Listener count: " + listenerCount.get());
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: for distributed caches, ensure that it is executed just on owner OR if event.isCommandRetried
|
// TODO: for distributed caches, ensure that it is executed just on owner OR if event.isCommandRetried
|
||||||
origCache
|
origCache
|
||||||
.getAdvancedCache().withFlags(Flag.SKIP_CACHE_LOAD, Flag.SKIP_CACHE_STORE)
|
.getAdvancedCache().withFlags(Flag.SKIP_CACHE_LOAD, Flag.SKIP_CACHE_STORE)
|
||||||
|
|
|
@ -60,8 +60,9 @@ class TestCacheManagerFactory {
|
||||||
private <T extends RemoteStoreConfigurationBuilder> Configuration getCacheBackedByRemoteStore(int threadId, String cacheName, Class<T> builderClass) {
|
private <T extends RemoteStoreConfigurationBuilder> Configuration getCacheBackedByRemoteStore(int threadId, String cacheName, Class<T> builderClass) {
|
||||||
ConfigurationBuilder cacheConfigBuilder = new ConfigurationBuilder();
|
ConfigurationBuilder cacheConfigBuilder = new ConfigurationBuilder();
|
||||||
|
|
||||||
|
String host = "localhost";
|
||||||
int port = threadId==1 ? 12232 : 13232;
|
int port = threadId==1 ? 12232 : 13232;
|
||||||
//int port = 12232;
|
//int port = 11222;
|
||||||
|
|
||||||
return cacheConfigBuilder.persistence().addStore(builderClass)
|
return cacheConfigBuilder.persistence().addStore(builderClass)
|
||||||
.fetchPersistentState(false)
|
.fetchPersistentState(false)
|
||||||
|
@ -74,7 +75,7 @@ class TestCacheManagerFactory {
|
||||||
.forceReturnValues(false)
|
.forceReturnValues(false)
|
||||||
.marshaller(KeycloakHotRodMarshallerFactory.class.getName())
|
.marshaller(KeycloakHotRodMarshallerFactory.class.getName())
|
||||||
.addServer()
|
.addServer()
|
||||||
.host("localhost")
|
.host(host)
|
||||||
.port(port)
|
.port(port)
|
||||||
.connectionPool()
|
.connectionPool()
|
||||||
.maxActive(20)
|
.maxActive(20)
|
||||||
|
|
|
@ -173,7 +173,8 @@ public class AuthServerTestEnricher {
|
||||||
if (suiteContext.getDcAuthServerBackendsInfo().stream().anyMatch(List::isEmpty)) {
|
if (suiteContext.getDcAuthServerBackendsInfo().stream().anyMatch(List::isEmpty)) {
|
||||||
throw new RuntimeException(String.format("Some data center has no auth server container matching '%s' defined in arquillian.xml.", AUTH_SERVER_BACKEND));
|
throw new RuntimeException(String.format("Some data center has no auth server container matching '%s' defined in arquillian.xml.", AUTH_SERVER_BACKEND));
|
||||||
}
|
}
|
||||||
if (suiteContext.getCacheServersInfo().isEmpty()) {
|
boolean cacheServerLifecycleSkip = Boolean.parseBoolean(System.getProperty("cache.server.lifecycle.skip"));
|
||||||
|
if (suiteContext.getCacheServersInfo().isEmpty() && !cacheServerLifecycleSkip) {
|
||||||
throw new IllegalStateException("Cache containers misconfiguration");
|
throw new IllegalStateException("Cache containers misconfiguration");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,13 @@ import org.hamcrest.Matchers;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserSessionRepresentation;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
import org.keycloak.testsuite.Retry;
|
import org.keycloak.testsuite.Retry;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
@ -264,6 +266,80 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// USER OPERATIONS
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserRemoveSessions(
|
||||||
|
@JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
|
||||||
|
@JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
|
||||||
|
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
|
||||||
|
createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics);
|
||||||
|
|
||||||
|
// log.infof("Sleeping!");
|
||||||
|
// Thread.sleep(10000000);
|
||||||
|
|
||||||
|
channelStatisticsCrossDc.reset();
|
||||||
|
|
||||||
|
// Remove test user
|
||||||
|
ApiUtil.findUserByUsernameId(getAdminClient().realm(REALM_NAME), "login-test").remove();
|
||||||
|
|
||||||
|
|
||||||
|
// Assert sessions removed on node1 and node2 and on remote caches. Assert that count of messages sent between DCs is not too big.
|
||||||
|
assertStatisticsExpected("After user remove", InfinispanConnectionProvider.SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
|
||||||
|
sessions01, sessions02, remoteSessions01, remoteSessions02, 40l);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserRemoveOfflineSessions(
|
||||||
|
@JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
|
||||||
|
@JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
|
||||||
|
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
|
||||||
|
createInitialSessions(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, true, cacheDc1Statistics, cacheDc2Statistics);
|
||||||
|
|
||||||
|
// log.infof("Sleeping!");
|
||||||
|
// Thread.sleep(10000000);
|
||||||
|
|
||||||
|
channelStatisticsCrossDc.reset();
|
||||||
|
|
||||||
|
// Remove test user
|
||||||
|
ApiUtil.findUserByUsernameId(getAdminClient().realm(REALM_NAME), "login-test").remove();
|
||||||
|
|
||||||
|
|
||||||
|
// Assert sessions removed on node1 and node2 and on remote caches. Assert that count of messages sent between DCs is not too big.
|
||||||
|
assertStatisticsExpected("After user remove", InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
|
||||||
|
sessions01, sessions02, remoteSessions01, remoteSessions02, 40l);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLogoutUser(
|
||||||
|
@JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
|
||||||
|
@JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
|
||||||
|
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
|
||||||
|
|
||||||
|
createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics);
|
||||||
|
|
||||||
|
channelStatisticsCrossDc.reset();
|
||||||
|
|
||||||
|
// Logout single session of user first
|
||||||
|
UserResource user = ApiUtil.findUserByUsernameId(getAdminClient().realm(REALM_NAME), "login-test");
|
||||||
|
UserSessionRepresentation userSession = user.getUserSessions().get(0);
|
||||||
|
getAdminClient().realm(REALM_NAME).deleteSession(userSession.getId());
|
||||||
|
|
||||||
|
// Just one session expired. Limit 5 for sent_messages is just if "lastSessionRefresh" periodic thread happened
|
||||||
|
assertStatisticsExpected("After logout single session", InfinispanConnectionProvider.SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
|
||||||
|
sessions01 + SESSIONS_COUNT - 1, sessions02 + SESSIONS_COUNT - 1, remoteSessions01 + SESSIONS_COUNT - 1, remoteSessions02 + SESSIONS_COUNT - 1, 5l);
|
||||||
|
|
||||||
|
// Logout all sessions for user now
|
||||||
|
user.logout();
|
||||||
|
|
||||||
|
// Assert sessions removed on node1 and node2 and on remote caches. Assert that count of messages sent between DCs is not too big.
|
||||||
|
assertStatisticsExpected("After user logout", InfinispanConnectionProvider.SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
|
||||||
|
sessions01, sessions02, remoteSessions01, remoteSessions02, 40l);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// AUTH SESSIONS
|
// AUTH SESSIONS
|
||||||
|
|
|
@ -17,16 +17,16 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.util.cli;
|
package org.keycloak.testsuite.util.cli;
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.infinispan.AdvancedCache;
|
import org.infinispan.AdvancedCache;
|
||||||
import org.infinispan.Cache;
|
import org.infinispan.Cache;
|
||||||
import org.infinispan.context.Flag;
|
import org.infinispan.context.Flag;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
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.sessions.infinispan.changes.SessionEntityWrapper;
|
import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
|
||||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||||
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
|
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
|
||||||
|
@ -318,16 +318,20 @@ public abstract class AbstractSessionCacheCommand extends AbstractCommand {
|
||||||
@Override
|
@Override
|
||||||
protected void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntityWrapper> cache) {
|
protected void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntityWrapper> cache) {
|
||||||
String realmName = getArg(1);
|
String realmName = getArg(1);
|
||||||
String username = getArg(2);
|
String clientId = getArg(2);
|
||||||
int count = getIntArg(3);
|
String username = getArg(3);
|
||||||
int batchCount = getIntArg(4);
|
int count = getIntArg(4);
|
||||||
|
int batchCount = getIntArg(5);
|
||||||
|
|
||||||
BatchTaskRunner.runInBatches(0, count, batchCount, session.getKeycloakSessionFactory(), (KeycloakSession batchSession, int firstInIteration, int countInIteration) -> {
|
BatchTaskRunner.runInBatches(0, count, batchCount, session.getKeycloakSessionFactory(), (KeycloakSession batchSession, int firstInIteration, int countInIteration) -> {
|
||||||
RealmModel realm = batchSession.realms().getRealmByName(realmName);
|
RealmModel realm = batchSession.realms().getRealmByName(realmName);
|
||||||
|
ClientModel client = realm.getClientByClientId(clientId);
|
||||||
UserModel user = batchSession.users().getUserByUsername(username, realm);
|
UserModel user = batchSession.users().getUserByUsername(username, realm);
|
||||||
|
|
||||||
for (int i=0 ; i<countInIteration ; i++) {
|
for (int i=0 ; i<countInIteration ; i++) {
|
||||||
session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, user, username, "127.0.0.1", "form", false, null, null);
|
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, user, username, "127.0.0.1", "form", false, null, null);
|
||||||
|
|
||||||
|
session.sessions().createClientSession(userSession.getRealm(), client, userSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.infof("Created '%d' sessions started from offset '%d'", countInIteration, firstInIteration);
|
log.infof("Created '%d' sessions started from offset '%d'", countInIteration, firstInIteration);
|
||||||
|
@ -338,7 +342,7 @@ public abstract class AbstractSessionCacheCommand extends AbstractCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String printUsage() {
|
public String printUsage() {
|
||||||
return getName() + " <cache-name> <realm-name> <user-name> <count> <count-in-batch>";
|
return getName() + " <cache-name> <realm-name> <client-id> <user-name> <count> <count-in-batch>";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue