clientSessionUpdateTx) {
+ if (userSession == null) {
+ throw new NullPointerException("userSession must not be null");
+ }
- public AuthenticatedClientSessionAdapter(AuthenticatedClientSessionEntity entity, ClientModel client, UserSessionAdapter userSession,
- InfinispanUserSessionProvider provider, InfinispanChangelogBasedTransaction updateTx) {
- this.provider = provider;
this.entity = entity;
- this.client = client;
- this.updateTx = updateTx;
this.userSession = userSession;
+ this.client = client;
+ this.userSessionUpdateTx = userSessionUpdateTx;
+ this.clientSessionUpdateTx = clientSessionUpdateTx;
}
private void update(UserSessionUpdateTask task) {
- updateTx.addTask(userSession.getId(), task);
+ userSessionUpdateTx.addTask(userSession.getId(), task);
}
+ private void update(ClientSessionUpdateTask task) {
+ clientSessionUpdateTx.addTask(entity.getId(), task);
+ }
+ /**
+ * Detaches the client session from its user session.
+ *
+ * This method does not delete the client session from user session records, it only removes the client session.
+ * The list of client sessions within user session is updated lazily for performance reasons.
+ */
@Override
- public void setUserSession(UserSessionModel userSession) {
- String clientUUID = client.getId();
+ public void detachFromUserSession() {
+ // Intentionally do not remove the clientUUID from the user session, invalid session is handled
+ // as nonexistent in org.keycloak.models.sessions.infinispan.UserSessionAdapter.getAuthenticatedClientSessions()
+ this.userSession = null;
- // Dettach userSession
- if (userSession == null) {
- UserSessionUpdateTask task = new UserSessionUpdateTask() {
+ SessionUpdateTask removeTask = Tasks.removeSync();
- @Override
- public void runUpdate(UserSessionEntity sessionEntity) {
- sessionEntity.getAuthenticatedClientSessions().remove(clientUUID);
- }
-
- };
- update(task);
- this.userSession = null;
- } else {
- this.userSession = (UserSessionAdapter) userSession;
- UserSessionUpdateTask task = new UserSessionUpdateTask() {
-
- @Override
- public void runUpdate(UserSessionEntity sessionEntity) {
- AuthenticatedClientSessionEntity current = sessionEntity.getAuthenticatedClientSessions().putIfAbsent(clientUUID, entity);
- if (current != null) {
- // It may happen when 2 concurrent HTTP requests trying SSO login against same client
- entity = current;
- }
- }
-
- };
- update(task);
- }
+ clientSessionUpdateTx.addTask(entity.getId(), removeTask);
}
@Override
@@ -104,10 +101,10 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
@Override
public void setRedirectUri(String uri) {
- UserSessionClientSessionUpdateTask task = new UserSessionClientSessionUpdateTask(client.getId()) {
+ ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
@Override
- protected void runClientSessionUpdate(AuthenticatedClientSessionEntity entity) {
+ public void runUpdate(AuthenticatedClientSessionEntity entity) {
entity.setRedirectUri(uri);
}
@@ -138,19 +135,12 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
@Override
public void setTimestamp(int timestamp) {
- UserSessionClientSessionUpdateTask task = new UserSessionClientSessionUpdateTask(client.getId()) {
+ ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
@Override
- protected void runClientSessionUpdate(AuthenticatedClientSessionEntity entity) {
+ public void runUpdate(AuthenticatedClientSessionEntity entity) {
entity.setTimestamp(timestamp);
}
-
- @Override
- public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
- // We usually update lastSessionRefresh at the same time. That would handle it.
- return CrossDCMessageStatus.NOT_NEEDED;
- }
-
};
update(task);
@@ -163,19 +153,12 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
@Override
public void setCurrentRefreshTokenUseCount(int currentRefreshTokenUseCount) {
- UserSessionClientSessionUpdateTask task = new UserSessionClientSessionUpdateTask(client.getId()) {
+ ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
@Override
- protected void runClientSessionUpdate(AuthenticatedClientSessionEntity entity) {
+ public void runUpdate(AuthenticatedClientSessionEntity entity) {
entity.setCurrentRefreshTokenUseCount(currentRefreshTokenUseCount);
}
-
- @Override
- public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
- // We usually update lastSessionRefresh at the same time. That would handle it.
- return CrossDCMessageStatus.NOT_NEEDED;
- }
-
};
update(task);
@@ -188,19 +171,12 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
@Override
public void setCurrentRefreshToken(String currentRefreshToken) {
- UserSessionClientSessionUpdateTask task = new UserSessionClientSessionUpdateTask(client.getId()) {
+ ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
@Override
- protected void runClientSessionUpdate(AuthenticatedClientSessionEntity entity) {
+ public void runUpdate(AuthenticatedClientSessionEntity entity) {
entity.setCurrentRefreshToken(currentRefreshToken);
}
-
- @Override
- public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
- // We usually update lastSessionRefresh at the same time. That would handle it.
- return CrossDCMessageStatus.NOT_NEEDED;
- }
-
};
update(task);
@@ -213,10 +189,10 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
@Override
public void setAction(String action) {
- UserSessionClientSessionUpdateTask task = new UserSessionClientSessionUpdateTask(client.getId()) {
+ ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
@Override
- protected void runClientSessionUpdate(AuthenticatedClientSessionEntity entity) {
+ public void runUpdate(AuthenticatedClientSessionEntity entity) {
entity.setAction(action);
}
@@ -232,10 +208,10 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
@Override
public void setProtocol(String method) {
- UserSessionClientSessionUpdateTask task = new UserSessionClientSessionUpdateTask(client.getId()) {
+ ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
@Override
- protected void runClientSessionUpdate(AuthenticatedClientSessionEntity entity) {
+ public void runUpdate(AuthenticatedClientSessionEntity entity) {
entity.setAuthMethod(method);
}
@@ -251,10 +227,10 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
@Override
public void setRoles(Set roles) {
- UserSessionClientSessionUpdateTask task = new UserSessionClientSessionUpdateTask(client.getId()) {
+ ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
@Override
- protected void runClientSessionUpdate(AuthenticatedClientSessionEntity entity) {
+ public void runUpdate(AuthenticatedClientSessionEntity entity) {
entity.setRoles(roles); // TODO not thread-safe. But we will remove setRoles anyway...?
}
@@ -270,10 +246,10 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
@Override
public void setProtocolMappers(Set protocolMappers) {
- UserSessionClientSessionUpdateTask task = new UserSessionClientSessionUpdateTask(client.getId()) {
+ ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
@Override
- protected void runClientSessionUpdate(AuthenticatedClientSessionEntity entity) {
+ public void runUpdate(AuthenticatedClientSessionEntity entity) {
entity.setProtocolMappers(protocolMappers); // TODO not thread-safe. But we will remove setProtocolMappers anyway...?
}
@@ -289,10 +265,10 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
@Override
public void setNote(String name, String value) {
- UserSessionClientSessionUpdateTask task = new UserSessionClientSessionUpdateTask(client.getId()) {
+ ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
@Override
- protected void runClientSessionUpdate(AuthenticatedClientSessionEntity entity) {
+ public void runUpdate(AuthenticatedClientSessionEntity entity) {
entity.getNotes().put(name, value);
}
@@ -303,10 +279,10 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
@Override
public void removeNote(String name) {
- UserSessionClientSessionUpdateTask task = new UserSessionClientSessionUpdateTask(client.getId()) {
+ ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
@Override
- protected void runClientSessionUpdate(AuthenticatedClientSessionEntity entity) {
+ public void runUpdate(AuthenticatedClientSessionEntity entity) {
entity.getNotes().remove(name);
}
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 8b6df90c3d..2c0411ec48 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
@@ -23,7 +23,6 @@ import org.infinispan.context.Flag;
import org.jboss.logging.Logger;
import org.keycloak.cluster.ClusterProvider;
import org.keycloak.common.util.Time;
-import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
@@ -33,12 +32,16 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.session.UserSessionPersisterProvider;
+import org.keycloak.models.sessions.infinispan.changes.Tasks;
import org.keycloak.models.sessions.infinispan.changes.sessions.LastSessionRefreshStore;
import org.keycloak.models.sessions.infinispan.remotestore.RemoteCacheInvoker;
import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
import org.keycloak.models.sessions.infinispan.changes.InfinispanChangelogBasedTransaction;
import org.keycloak.models.sessions.infinispan.changes.SessionUpdateTask;
+import org.keycloak.models.sessions.infinispan.changes.SessionUpdateTask.CacheOperation;
+import org.keycloak.models.sessions.infinispan.changes.SessionUpdateTask.CrossDCMessageStatus;
import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
+import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionStore;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
@@ -58,8 +61,10 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -75,10 +80,14 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
protected final Cache> sessionCache;
protected final Cache> offlineSessionCache;
+ protected final Cache> clientSessionCache;
+ protected final Cache> offlineClientSessionCache;
protected final Cache> loginFailureCache;
protected final InfinispanChangelogBasedTransaction sessionTx;
protected final InfinispanChangelogBasedTransaction offlineSessionTx;
+ protected final InfinispanChangelogBasedTransaction clientSessionTx;
+ protected final InfinispanChangelogBasedTransaction offlineClientSessionTx;
protected final InfinispanChangelogBasedTransaction loginFailuresTx;
protected final SessionEventsSenderTransaction clusterEventsSenderTx;
@@ -92,17 +101,23 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
LastSessionRefreshStore offlineLastSessionRefreshStore,
Cache> sessionCache,
Cache> offlineSessionCache,
+ Cache> clientSessionCache,
+ Cache> offlineClientSessionCache,
Cache> loginFailureCache) {
this.session = session;
this.sessionCache = sessionCache;
+ this.clientSessionCache = clientSessionCache;
this.offlineSessionCache = offlineSessionCache;
+ this.offlineClientSessionCache = offlineClientSessionCache;
this.loginFailureCache = loginFailureCache;
- this.sessionTx = new InfinispanChangelogBasedTransaction<>(session, InfinispanConnectionProvider.SESSION_CACHE_NAME, sessionCache, remoteCacheInvoker);
- this.offlineSessionTx = new InfinispanChangelogBasedTransaction<>(session, InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, offlineSessionCache, remoteCacheInvoker);
+ this.sessionTx = new InfinispanChangelogBasedTransaction<>(session, sessionCache, remoteCacheInvoker);
+ this.offlineSessionTx = new InfinispanChangelogBasedTransaction<>(session, offlineSessionCache, remoteCacheInvoker);
+ this.clientSessionTx = new InfinispanChangelogBasedTransaction<>(session, clientSessionCache, remoteCacheInvoker);
+ this.offlineClientSessionTx = new InfinispanChangelogBasedTransaction<>(session, offlineClientSessionCache, remoteCacheInvoker);
- this.loginFailuresTx = new InfinispanChangelogBasedTransaction<>(session, InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, loginFailureCache, remoteCacheInvoker);
+ this.loginFailuresTx = new InfinispanChangelogBasedTransaction<>(session, loginFailureCache, remoteCacheInvoker);
this.clusterEventsSenderTx = new SessionEventsSenderTransaction(session);
@@ -112,6 +127,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
session.getTransactionManager().enlistAfterCompletion(clusterEventsSenderTx);
session.getTransactionManager().enlistAfterCompletion(sessionTx);
session.getTransactionManager().enlistAfterCompletion(offlineSessionTx);
+ session.getTransactionManager().enlistAfterCompletion(clientSessionTx);
+ session.getTransactionManager().enlistAfterCompletion(offlineClientSessionTx);
session.getTransactionManager().enlistAfterCompletion(loginFailuresTx);
}
@@ -123,6 +140,14 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
return offline ? offlineSessionTx : sessionTx;
}
+ protected Cache> getClientSessionCache(boolean offline) {
+ return offline ? offlineClientSessionCache : clientSessionCache;
+ }
+
+ protected InfinispanChangelogBasedTransaction getClientSessionTransaction(boolean offline) {
+ return offline ? offlineClientSessionTx : clientSessionTx;
+ }
+
protected LastSessionRefreshStore getLastSessionRefreshStore() {
return lastSessionRefreshStore;
}
@@ -134,10 +159,20 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
@Override
public AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession) {
AuthenticatedClientSessionEntity entity = new AuthenticatedClientSessionEntity();
+ entity.setRealmId(realm.getId());
+ final UUID clientSessionId = entity.getId();
+
+ InfinispanChangelogBasedTransaction userSessionUpdateTx = getTransaction(false);
+ InfinispanChangelogBasedTransaction clientSessionUpdateTx = getClientSessionTransaction(false);
+ AuthenticatedClientSessionAdapter adapter = new AuthenticatedClientSessionAdapter(entity, client, (UserSessionAdapter) userSession,
+ userSessionUpdateTx, clientSessionUpdateTx);
+
+ SessionUpdateTask createClientSessionTask = Tasks.addIfAbsentSync();
+ clientSessionUpdateTx.addTask(clientSessionId, createClientSessionTask, entity);
+
+ SessionUpdateTask registerClientSessionTask = new RegisterClientSessionTask(client.getId(), clientSessionId);
+ userSessionUpdateTx.addTask(userSession.getId(), registerClientSessionTask);
- InfinispanChangelogBasedTransaction updateTx = getTransaction(false);
- AuthenticatedClientSessionAdapter adapter = new AuthenticatedClientSessionAdapter(entity, client, (UserSessionAdapter) userSession, this, updateTx);
- adapter.setUserSession(userSession);
return adapter;
}
@@ -147,25 +182,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
entity.setId(id);
updateSessionEntity(entity, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId);
- SessionUpdateTask createSessionTask = new SessionUpdateTask() {
-
- @Override
- public void runUpdate(UserSessionEntity session) {
-
- }
-
- @Override
- public CacheOperation getOperation(UserSessionEntity session) {
- return CacheOperation.ADD_IF_ABSENT;
- }
-
- @Override
- public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
- return CrossDCMessageStatus.SYNC;
- }
-
- };
-
+ SessionUpdateTask createSessionTask = Tasks.addIfAbsentSync();
sessionTx.addTask(id, createSessionTask, entity);
return wrap(realm, entity, false);
@@ -228,6 +245,19 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
return resultSessions;
}
+ @Override
+ public AuthenticatedClientSessionAdapter getClientSession(UserSessionModel userSession, ClientModel client, UUID clientSessionId, boolean offline) {
+ AuthenticatedClientSessionEntity entity = getClientSessionEntity(clientSessionId, offline);
+ return wrap(userSession, client, entity, offline);
+ }
+
+ private AuthenticatedClientSessionEntity getClientSessionEntity(UUID id, boolean offline) {
+ InfinispanChangelogBasedTransaction tx = getClientSessionTransaction(offline);
+ SessionEntityWrapper entityWrapper = tx.get(id);
+ return entityWrapper == null ? null : entityWrapper.getEntity();
+ }
+
+
@Override
public List getUserSessions(final RealmModel realm, UserModel user) {
return getUserSessions(realm, UserSessionPredicate.create(realm.getId()).user(user.getId()), false);
@@ -256,12 +286,21 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
protected List getUserSessions(final RealmModel realm, ClientModel client, int firstResult, int maxResults, final boolean offline) {
Cache> cache = getCache(offline);
-
cache = CacheDecorators.skipCacheLoaders(cache);
+ Cache> clientSessionCache = getClientSessionCache(offline);
+ Cache> clientSessionCacheDecorated = CacheDecorators.skipCacheLoaders(clientSessionCache);
+
+ final String clientUuid = client.getId();
+
Stream stream = cache.entrySet().stream()
- .filter(UserSessionPredicate.create(realm.getId()).client(client.getId()))
+ .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) {
@@ -356,8 +395,19 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
Cache> cache = getCache(offline);
cache = CacheDecorators.skipCacheLoaders(cache);
+ Cache> clientSessionCache = getClientSessionCache(offline);
+ Cache> clientSessionCacheDecorated = CacheDecorators.skipCacheLoaders(clientSessionCache);
+
+ final String clientUuid = client.getId();
+
return cache.entrySet().stream()
- .filter(UserSessionPredicate.create(realm.getId()).client(client.getId()))
+ .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();
}
@@ -402,28 +452,38 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
// Each cluster node cleanups just local sessions, which are those owned by itself (+ few more taking l1 cache into account)
Cache> localCache = CacheDecorators.localCache(sessionCache);
+ Cache> localClientSessionCache = CacheDecorators.localCache(offlineClientSessionCache);
Cache> localCacheStoreIgnore = CacheDecorators.skipCacheLoaders(localCache);
+ final AtomicInteger userSessionsSize = new AtomicInteger();
+
// Ignore remoteStore for stream iteration. But we will invoke remoteStore for userSession removal propagate
localCacheStoreIgnore
.entrySet()
.stream()
.filter(UserSessionPredicate.create(realm.getId()).expired(expired, expiredRefresh))
- .map(Mappers.sessionId())
- .forEach(new Consumer() {
+ .map(Mappers.userSessionEntity())
+ .forEach(new Consumer() {
@Override
- public void accept(String sessionId) {
- Future future = localCache.removeAsync(sessionId);
+ public void accept(UserSessionEntity userSessionEntity) {
+ userSessionsSize.incrementAndGet();
+
+ Future future = localCache.removeAsync(userSessionEntity.getId());
futures.addTask(future);
+
+ userSessionEntity.getAuthenticatedClientSessions().forEach((clientUUID, clientSessionId) -> {
+ Future f = localClientSessionCache.removeAsync(clientSessionId);
+ futures.addTask(f);
+ });
}
});
futures.waitForAllToFinish();
- log.debugf("Removed %d expired user sessions for realm '%s'", futures.size(), realm.getName());
+ log.debugf("Removed %d expired user sessions for realm '%s'", userSessionsSize.get(), realm.getName());
}
private void removeExpiredOfflineUserSessions(RealmModel realm) {
@@ -432,6 +492,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
// Each cluster node cleanups just local sessions, which are those owned by himself (+ few more taking l1 cache into account)
Cache> localCache = CacheDecorators.localCache(offlineSessionCache);
+ Cache> localClientSessionCache = CacheDecorators.localCache(offlineClientSessionCache);
UserSessionPredicate predicate = UserSessionPredicate.create(realm.getId()).expired(null, expiredOffline);
@@ -439,6 +500,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
Cache> localCacheStoreIgnore = CacheDecorators.skipCacheLoaders(localCache);
+ final AtomicInteger userSessionsSize = new AtomicInteger();
+
// Ignore remoteStore for stream iteration. But we will invoke remoteStore for userSession removal propagate
localCacheStoreIgnore
.entrySet()
@@ -449,8 +512,14 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
@Override
public void accept(UserSessionEntity userSessionEntity) {
+ userSessionsSize.incrementAndGet();
+
Future future = localCache.removeAsync(userSessionEntity.getId());
futures.addTask(future);
+ userSessionEntity.getAuthenticatedClientSessions().forEach((clientUUID, clientSessionId) -> {
+ Future f = localClientSessionCache.removeAsync(clientSessionId);
+ futures.addTask(f);
+ });
// TODO:mposolda can be likely optimized to delete all expired at one step
persister.removeUserSession( userSessionEntity.getId(), true);
@@ -464,7 +533,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
futures.waitForAllToFinish();
- log.debugf("Removed %d expired offline user sessions for realm '%s'", futures.size(), realm.getName());
+ log.debugf("Removed %d expired offline user sessions for realm '%s'", userSessionsSize.get(), realm.getName());
}
@Override
@@ -484,28 +553,39 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
Cache> cache = getCache(offline);
Cache> localCache = CacheDecorators.localCache(cache);
+ Cache> clientSessionCache = getClientSessionCache(offline);
+ Cache> localClientSessionCache = CacheDecorators.localCache(clientSessionCache);
Cache> localCacheStoreIgnore = CacheDecorators.skipCacheLoaders(localCache);
+ final AtomicInteger userSessionsSize = new AtomicInteger();
+
localCacheStoreIgnore
.entrySet()
.stream()
.filter(SessionPredicate.create(realmId))
- .map(Mappers.sessionId())
- .forEach(new Consumer() {
+ .map(Mappers.userSessionEntity())
+ .forEach(new Consumer() {
@Override
- public void accept(String sessionId) {
+ public void accept(UserSessionEntity userSessionEntity) {
+ userSessionsSize.incrementAndGet();
+
// Remove session from remoteCache too. Use removeAsync for better perf
- Future future = localCache.removeAsync(sessionId);
+ Future future = localCache.removeAsync(userSessionEntity.getId());
futures.addTask(future);
+ userSessionEntity.getAuthenticatedClientSessions().forEach((clientUUID, clientSessionId) -> {
+ Future f = localClientSessionCache.removeAsync(clientSessionId);
+ futures.addTask(f);
+ });
}
});
+
futures.waitForAllToFinish();
- log.debugf("Removed %d sessions in realm %s. Offline: %b", (Object) futures.size(), realmId, offline);
+ log.debugf("Removed %d sessions in realm %s. Offline: %b", (Object) userSessionsSize.get(), realmId, offline);
}
@Override
@@ -528,25 +608,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
entity.setRealmId(realm.getId());
entity.setUserId(userId);
- SessionUpdateTask createLoginFailureTask = new SessionUpdateTask() {
-
- @Override
- public void runUpdate(LoginFailureEntity session) {
-
- }
-
- @Override
- public CacheOperation getOperation(LoginFailureEntity session) {
- return CacheOperation.ADD_IF_ABSENT;
- }
-
- @Override
- public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
- return CrossDCMessageStatus.SYNC;
- }
-
- };
-
+ SessionUpdateTask createLoginFailureTask = Tasks.addIfAbsentSync();
loginFailuresTx.addTask(key, createLoginFailureTask, entity);
return wrap(key, entity);
@@ -554,25 +616,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
@Override
public void removeUserLoginFailure(RealmModel realm, String userId) {
- SessionUpdateTask removeTask = new SessionUpdateTask() {
-
- @Override
- public void runUpdate(LoginFailureEntity entity) {
-
- }
-
- @Override
- public CacheOperation getOperation(LoginFailureEntity entity) {
- return CacheOperation.REMOVE;
- }
-
- @Override
- public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
- return CrossDCMessageStatus.SYNC;
- }
-
- };
-
+ SessionUpdateTask removeTask = Tasks.removeSync();
loginFailuresTx.addTask(new LoginFailureKey(realm.getId(), userId), removeTask);
}
@@ -648,28 +692,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
protected void removeUserSession(UserSessionEntity sessionEntity, boolean offline) {
- InfinispanChangelogBasedTransaction tx = getTransaction(offline);
-
- SessionUpdateTask removeTask = new SessionUpdateTask() {
-
- @Override
- public void runUpdate(UserSessionEntity entity) {
-
- }
-
- @Override
- public CacheOperation getOperation(UserSessionEntity entity) {
- return CacheOperation.REMOVE;
- }
-
- @Override
- public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
- return CrossDCMessageStatus.SYNC;
- }
-
- };
-
- tx.addTask(sessionEntity.getId(), removeTask);
+ InfinispanChangelogBasedTransaction userSessionUpdateTx = getTransaction(offline);
+ InfinispanChangelogBasedTransaction clientSessionUpdateTx = getClientSessionTransaction(offline);
+ sessionEntity.getAuthenticatedClientSessions().forEach((clientUUID, clientSessionId) -> clientSessionUpdateTx.addTask(clientSessionId, Tasks.removeSync()));
+ SessionUpdateTask removeTask = Tasks.removeSync();
+ userSessionUpdateTx.addTask(sessionEntity.getId(), removeTask);
}
InfinispanChangelogBasedTransaction getLoginFailuresTx() {
@@ -677,8 +704,15 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
UserSessionAdapter wrap(RealmModel realm, UserSessionEntity entity, boolean offline) {
- InfinispanChangelogBasedTransaction tx = getTransaction(offline);
- return entity != null ? new UserSessionAdapter(session, this, tx, realm, entity, offline) : null;
+ InfinispanChangelogBasedTransaction userSessionUpdateTx = getTransaction(offline);
+ InfinispanChangelogBasedTransaction clientSessionUpdateTx = getClientSessionTransaction(offline);
+ return entity != null ? new UserSessionAdapter(session, this, userSessionUpdateTx, clientSessionUpdateTx, realm, entity, offline) : null;
+ }
+
+ AuthenticatedClientSessionAdapter wrap(UserSessionModel userSession, ClientModel client, AuthenticatedClientSessionEntity entity, boolean offline) {
+ InfinispanChangelogBasedTransaction userSessionUpdateTx = getTransaction(offline);
+ InfinispanChangelogBasedTransaction clientSessionUpdateTx = getClientSessionTransaction(offline);
+ return entity != null ? new AuthenticatedClientSessionAdapter(entity, client, userSession, userSessionUpdateTx, clientSessionUpdateTx) : null;
}
UserLoginFailureModel wrap(LoginFailureKey key, LoginFailureEntity entity) {
@@ -726,7 +760,9 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
UserSessionAdapter userSessionAdapter = (offlineUserSession instanceof UserSessionAdapter) ? (UserSessionAdapter) offlineUserSession :
getOfflineUserSession(offlineUserSession.getRealm(), offlineUserSession.getId());
- AuthenticatedClientSessionAdapter offlineClientSession = importClientSession(userSessionAdapter, clientSession, getTransaction(true));
+ InfinispanChangelogBasedTransaction userSessionUpdateTx = getTransaction(true);
+ InfinispanChangelogBasedTransaction clientSessionUpdateTx = getClientSessionTransaction(true);
+ AuthenticatedClientSessionAdapter offlineClientSession = importClientSession(userSessionAdapter, clientSession, userSessionUpdateTx, clientSessionUpdateTx);
// update timestamp to current time
offlineClientSession.setTimestamp(Time.currentTime());
@@ -776,7 +812,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
entity.setIpAddress(userSession.getIpAddress());
entity.setLoginUsername(userSession.getLoginUsername());
entity.setNotes(userSession.getNotes() == null ? new ConcurrentHashMap<>() : userSession.getNotes());
- entity.setAuthenticatedClientSessions(new ConcurrentHashMap<>());
+ entity.setAuthenticatedClientSessions(new AuthenticatedClientSessionStore());
entity.setRememberMe(userSession.isRememberMe());
entity.setState(userSession.getState());
entity.setUser(userSession.getUser().getId());
@@ -784,35 +820,18 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
entity.setStarted(userSession.getStarted());
entity.setLastSessionRefresh(userSession.getLastSessionRefresh());
+ InfinispanChangelogBasedTransaction userSessionUpdateTx = getTransaction(offline);
+ InfinispanChangelogBasedTransaction clientSessionUpdateTx = getClientSessionTransaction(offline);
- InfinispanChangelogBasedTransaction tx = getTransaction(offline);
-
- SessionUpdateTask importTask = new SessionUpdateTask() {
-
- @Override
- public void runUpdate(UserSessionEntity session) {
-
- }
-
- @Override
- public CacheOperation getOperation(UserSessionEntity session) {
- return CacheOperation.ADD_IF_ABSENT;
- }
-
- @Override
- public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
- return CrossDCMessageStatus.SYNC;
- }
-
- };
- tx.addTask(userSession.getId(), importTask, entity);
+ SessionUpdateTask importTask = Tasks.addIfAbsentSync();
+ userSessionUpdateTx.addTask(userSession.getId(), importTask, entity);
UserSessionAdapter importedSession = wrap(userSession.getRealm(), entity, offline);
// Handle client sessions
if (importAuthenticatedClientSessions) {
for (AuthenticatedClientSessionModel clientSession : userSession.getAuthenticatedClientSessions().values()) {
- importClientSession(importedSession, clientSession, tx);
+ importClientSession(importedSession, clientSession, userSessionUpdateTx, clientSessionUpdateTx);
}
}
@@ -820,9 +839,12 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
- private AuthenticatedClientSessionAdapter importClientSession(UserSessionAdapter importedUserSession, AuthenticatedClientSessionModel clientSession,
- InfinispanChangelogBasedTransaction updateTx) {
+ private AuthenticatedClientSessionAdapter importClientSession(UserSessionAdapter sessionToImportInto, AuthenticatedClientSessionModel clientSession,
+ InfinispanChangelogBasedTransaction userSessionUpdateTx,
+ InfinispanChangelogBasedTransaction clientSessionUpdateTx) {
AuthenticatedClientSessionEntity entity = new AuthenticatedClientSessionEntity();
+ entity.setRealmId(sessionToImportInto.getRealm().getId());
+ final UUID clientSessionId = entity.getId();
entity.setAction(clientSession.getAction());
entity.setAuthMethod(clientSession.getProtocol());
@@ -833,33 +855,43 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
entity.setRoles(clientSession.getRoles());
entity.setTimestamp(clientSession.getTimestamp());
+ SessionUpdateTask createClientSessionTask = Tasks.addIfAbsentSync();
+ clientSessionUpdateTx.addTask(entity.getId(), createClientSessionTask, entity);
- Map clientSessions = importedUserSession.getEntity().getAuthenticatedClientSessions();
+ AuthenticatedClientSessionStore clientSessions = sessionToImportInto.getEntity().getAuthenticatedClientSessions();
+ clientSessions.put(clientSession.getClient().getId(), clientSessionId);
- clientSessions.put(clientSession.getClient().getId(), entity);
+ SessionUpdateTask registerClientSessionTask = new RegisterClientSessionTask(clientSession.getClient().getId(), clientSessionId);
+ userSessionUpdateTx.addTask(sessionToImportInto.getId(), registerClientSessionTask);
- SessionUpdateTask importTask = new SessionUpdateTask() {
+ return new AuthenticatedClientSessionAdapter(entity, clientSession.getClient(), sessionToImportInto, userSessionUpdateTx, clientSessionUpdateTx);
+ }
- @Override
- public void runUpdate(UserSessionEntity session) {
- Map clientSessions = session.getAuthenticatedClientSessions();
- clientSessions.put(clientSession.getClient().getId(), entity);
- }
+ private static class RegisterClientSessionTask implements SessionUpdateTask {
- @Override
- public CacheOperation getOperation(UserSessionEntity session) {
- return CacheOperation.REPLACE;
- }
+ private final String clientUuid;
+ private final UUID clientSessionId;
- @Override
- public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
- return CrossDCMessageStatus.SYNC;
- }
+ public RegisterClientSessionTask(String clientUuid, UUID clientSessionId) {
+ this.clientUuid = clientUuid;
+ this.clientSessionId = clientSessionId;
+ }
- };
- updateTx.addTask(importedUserSession.getId(), importTask);
+ @Override
+ public void runUpdate(UserSessionEntity session) {
+ AuthenticatedClientSessionStore clientSessions = session.getAuthenticatedClientSessions();
+ clientSessions.put(clientUuid, clientSessionId);
+ }
- return new AuthenticatedClientSessionAdapter(entity, clientSession.getClient(), importedUserSession, this, updateTx);
+ @Override
+ public CacheOperation getOperation(UserSessionEntity session) {
+ return CacheOperation.REPLACE;
+ }
+
+ @Override
+ public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
+ return CrossDCMessageStatus.SYNC;
+ }
}
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
index ccc9b84943..ae2b3d46cb 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
@@ -37,6 +37,7 @@ import org.keycloak.models.sessions.infinispan.initializer.CacheInitializer;
import org.keycloak.models.sessions.infinispan.initializer.DBLockBasedCacheInitializer;
import org.keycloak.models.sessions.infinispan.remotestore.RemoteCacheInvoker;
import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
+import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
@@ -58,6 +59,7 @@ import org.keycloak.provider.ProviderEventListener;
import java.io.Serializable;
import java.util.Set;
+import java.util.UUID;
public class InfinispanUserSessionProviderFactory implements UserSessionProviderFactory {
@@ -82,11 +84,14 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
@Override
public InfinispanUserSessionProvider create(KeycloakSession session) {
InfinispanConnectionProvider connections = session.getProvider(InfinispanConnectionProvider.class);
- Cache> cache = connections.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
- Cache> offlineSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME);
+ Cache> cache = connections.getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
+ Cache> offlineSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME);
+ Cache> clientSessionCache = connections.getCache(InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME);
+ Cache> offlineClientSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME);
Cache> loginFailures = connections.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME);
- return new InfinispanUserSessionProvider(session, remoteCacheInvoker, lastSessionRefreshStore, offlineLastSessionRefreshStore, cache, offlineSessionsCache, loginFailures);
+ return new InfinispanUserSessionProvider(session, remoteCacheInvoker, lastSessionRefreshStore, offlineLastSessionRefreshStore,
+ cache, offlineSessionsCache, clientSessionCache, offlineClientSessionsCache, loginFailures);
}
@Override
@@ -205,7 +210,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
InfinispanConnectionProvider ispn = session.getProvider(InfinispanConnectionProvider.class);
- Cache> sessionsCache = ispn.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
+ Cache> sessionsCache = ispn.getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
boolean sessionsRemoteCache = checkRemoteCache(session, sessionsCache, (RealmModel realm) -> {
return realm.getSsoSessionIdleTimeout() * 1000;
});
@@ -214,8 +219,12 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
lastSessionRefreshStore = new LastSessionRefreshStoreFactory().createAndInit(session, sessionsCache, false);
}
+ Cache> clientSessionsCache = ispn.getCache(InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME);
+ checkRemoteCache(session, clientSessionsCache, (RealmModel realm) -> {
+ return realm.getSsoSessionIdleTimeout() * 1000;
+ });
- Cache> offlineSessionsCache = ispn.getCache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME);
+ Cache> offlineSessionsCache = ispn.getCache(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME);
boolean offlineSessionsRemoteCache = checkRemoteCache(session, offlineSessionsCache, (RealmModel realm) -> {
return realm.getOfflineSessionIdleTimeout() * 1000;
});
@@ -224,8 +233,13 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
offlineLastSessionRefreshStore = new LastSessionRefreshStoreFactory().createAndInit(session, offlineSessionsCache, true);
}
+ Cache> offlineClientSessionsCache = ispn.getCache(InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME);
+ checkRemoteCache(session, offlineClientSessionsCache, (RealmModel realm) -> {
+ return realm.getOfflineSessionIdleTimeout() * 1000;
+ });
+
Cache> loginFailuresCache = ispn.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME);
- boolean loginFailuresRemoteCache = checkRemoteCache(session, loginFailuresCache, (RealmModel realm) -> {
+ checkRemoteCache(session, loginFailuresCache, (RealmModel realm) -> {
return realm.getMaxDeltaTimeSeconds() * 1000;
});
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
index 635d4f7a0c..de825573fe 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
@@ -26,15 +26,22 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.sessions.infinispan.changes.InfinispanChangelogBasedTransaction;
import org.keycloak.models.sessions.infinispan.changes.sessions.LastSessionRefreshChecker;
import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
+import org.keycloak.models.sessions.infinispan.changes.Tasks;
import org.keycloak.models.sessions.infinispan.changes.UserSessionUpdateTask;
import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
+import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionStore;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
/**
* @author Stian Thorgersen
@@ -45,7 +52,9 @@ public class UserSessionAdapter implements UserSessionModel {
private final InfinispanUserSessionProvider provider;
- private final InfinispanChangelogBasedTransaction updateTx;
+ private final InfinispanChangelogBasedTransaction userSessionUpdateTx;
+
+ private final InfinispanChangelogBasedTransaction clientSessionUpdateTx;
private final RealmModel realm;
@@ -53,11 +62,14 @@ public class UserSessionAdapter implements UserSessionModel {
private final boolean offline;
- public UserSessionAdapter(KeycloakSession session, InfinispanUserSessionProvider provider, InfinispanChangelogBasedTransaction updateTx, RealmModel realm,
- UserSessionEntity entity, boolean offline) {
+ public UserSessionAdapter(KeycloakSession session, InfinispanUserSessionProvider provider,
+ InfinispanChangelogBasedTransaction userSessionUpdateTx,
+ InfinispanChangelogBasedTransaction clientSessionUpdateTx,
+ RealmModel realm, UserSessionEntity entity, boolean offline) {
this.session = session;
this.provider = provider;
- this.updateTx = updateTx;
+ this.userSessionUpdateTx = userSessionUpdateTx;
+ this.clientSessionUpdateTx = clientSessionUpdateTx;
this.realm = realm;
this.entity = entity;
this.offline = offline;
@@ -65,17 +77,20 @@ public class UserSessionAdapter implements UserSessionModel {
@Override
public Map getAuthenticatedClientSessions() {
- Map clientSessionEntities = entity.getAuthenticatedClientSessions();
+ AuthenticatedClientSessionStore clientSessionEntities = entity.getAuthenticatedClientSessions();
Map result = new HashMap<>();
List removedClientUUIDS = new LinkedList<>();
if (clientSessionEntities != null) {
- clientSessionEntities.forEach((String key, AuthenticatedClientSessionEntity value) -> {
+ clientSessionEntities.forEach((String key, UUID value) -> {
// Check if client still exists
ClientModel client = realm.getClientById(key);
if (client != null) {
- result.put(key, new AuthenticatedClientSessionAdapter(value, client, this, provider, updateTx));
+ final AuthenticatedClientSessionAdapter clientSession = provider.getClientSession(this, client, value, offline);
+ if (clientSession != null) {
+ result.put(key, clientSession);
+ }
} else {
removedClientUUIDS.add(key);
}
@@ -88,20 +103,53 @@ public class UserSessionAdapter implements UserSessionModel {
}
@Override
- public void removeAuthenticatedClientSessions(Iterable removedClientUUIDS) {
- if (removedClientUUIDS == null || ! removedClientUUIDS.iterator().hasNext()) {
+ public AuthenticatedClientSessionModel getAuthenticatedClientSessionByClient(String clientUUID) {
+ AuthenticatedClientSessionStore clientSessionEntities = entity.getAuthenticatedClientSessions();
+ final UUID clientSessionId = clientSessionEntities.get(clientUUID);
+
+ if (clientSessionId == null) {
+ return null;
+ }
+
+ ClientModel client = realm.getClientById(clientUUID);
+
+ if (client != null) {
+ return provider.getClientSession(this, client, clientSessionId, offline);
+ }
+
+ removeAuthenticatedClientSessions(Collections.singleton(clientUUID));
+ return null;
+ }
+
+ private static final int MINIMUM_INACTIVE_CLIENT_SESSIONS_TO_CLEANUP = 5;
+
+ @Override
+ public void removeAuthenticatedClientSessions(Collection removedClientUUIDS) {
+ if (removedClientUUIDS == null || ! removedClientUUIDS.isEmpty()) {
return;
}
- // Update user session
- UserSessionUpdateTask task = new UserSessionUpdateTask() {
- @Override
- public void runUpdate(UserSessionEntity entity) {
- removedClientUUIDS.forEach(entity.getAuthenticatedClientSessions()::remove);
- }
- };
+ // Performance: do not remove the clientUUIDs from the user session until there is enough of them;
+ // an invalid session is handled as nonexistent in UserSessionAdapter.getAuthenticatedClientSessions()
+ if (removedClientUUIDS.size() >= MINIMUM_INACTIVE_CLIENT_SESSIONS_TO_CLEANUP) {
+ // Update user session
+ UserSessionUpdateTask task = new UserSessionUpdateTask() {
+ @Override
+ public void runUpdate(UserSessionEntity entity) {
+ removedClientUUIDS.forEach(entity.getAuthenticatedClientSessions()::remove);
+ }
+ };
+ update(task);
+ }
- update(task);
+ // do not iterate the removedClientUUIDS and remove the clientSession directly as the addTask can manipulate
+ // the collection being iterated, and that can lead to unpredictable behaviour (e.g. NPE)
+ List clientSessionUuids = removedClientUUIDS.stream()
+ .map(entity.getAuthenticatedClientSessions()::get)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+ clientSessionUuids.forEach(clientSessionId -> this.clientSessionUpdateTx.addTask(clientSessionId, Tasks.removeSync()));
}
public String getId() {
@@ -276,7 +324,7 @@ public class UserSessionAdapter implements UserSessionModel {
}
void update(UserSessionUpdateTask task) {
- updateTx.addTask(getId(), task);
+ userSessionUpdateTx.addTask(getId(), task);
}
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/ClientSessionUpdateTask.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/ClientSessionUpdateTask.java
new file mode 100644
index 0000000000..f587c45142
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/ClientSessionUpdateTask.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.sessions.infinispan.changes;
+
+import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
+
+/**
+ * @author Marek Posolda
+ */
+public abstract class ClientSessionUpdateTask implements SessionUpdateTask {
+
+ @Override
+ public CacheOperation getOperation(AuthenticatedClientSessionEntity session) {
+ return CacheOperation.REPLACE;
+ }
+
+ @Override
+ public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
+ return CrossDCMessageStatus.SYNC;
+ }
+
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java
index 694c41e461..2a0b4362bc 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java
@@ -45,9 +45,9 @@ public class InfinispanChangelogBasedTransaction ext
private final Map> updates = new HashMap<>();
- public InfinispanChangelogBasedTransaction(KeycloakSession kcSession, String cacheName, Cache> cache, RemoteCacheInvoker remoteCacheInvoker) {
+ public InfinispanChangelogBasedTransaction(KeycloakSession kcSession, Cache> cache, RemoteCacheInvoker remoteCacheInvoker) {
this.kcSession = kcSession;
- this.cacheName = cacheName;
+ this.cacheName = cache.getName();
this.cache = cache;
this.remoteCacheInvoker = remoteCacheInvoker;
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/Tasks.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/Tasks.java
new file mode 100644
index 0000000000..214e2e9256
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/Tasks.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.sessions.infinispan.changes;
+
+import org.keycloak.models.sessions.infinispan.changes.SessionUpdateTask.CacheOperation;
+import org.keycloak.models.sessions.infinispan.changes.SessionUpdateTask.CrossDCMessageStatus;
+import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class Tasks {
+
+ private static final SessionUpdateTask extends SessionEntity> ADD_IF_ABSENT_SYNC = new SessionUpdateTask() {
+ @Override
+ public void runUpdate(SessionEntity entity) {
+ }
+
+ @Override
+ public CacheOperation getOperation(SessionEntity entity) {
+ return CacheOperation.ADD_IF_ABSENT;
+ }
+
+ @Override
+ public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
+ return CrossDCMessageStatus.SYNC;
+ }
+ };
+
+ private static final SessionUpdateTask extends SessionEntity> REMOVE_SYNC = new SessionUpdateTask() {
+ @Override
+ public void runUpdate(SessionEntity entity) {
+ }
+
+ @Override
+ public CacheOperation getOperation(SessionEntity entity) {
+ return CacheOperation.REMOVE;
+ }
+
+ @Override
+ public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper sessionWrapper) {
+ return CrossDCMessageStatus.SYNC;
+ }
+ };
+
+ /**
+ * Returns a typed task of type {@link CacheOperation#ADD_IF_ABSENT} that does no other update. This operation has DC message
+ * status {@link CrossDCMessageStatus#SYNC}.
+ * @param
+ * @return
+ */
+ public static SessionUpdateTask addIfAbsentSync() {
+ return (SessionUpdateTask) ADD_IF_ABSENT_SYNC;
+ }
+
+ /**
+ * Returns a typed task of type {@link CacheOperation#REMOVE} that does no other update. This operation has DC message
+ * status {@link CrossDCMessageStatus#SYNC}.
+ * @param
+ * @return
+ */
+ public static SessionUpdateTask removeSync() {
+ return (SessionUpdateTask) REMOVE_SYNC;
+ }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/UserSessionClientSessionUpdateTask.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/UserSessionClientSessionUpdateTask.java
deleted file mode 100644
index 56e0403143..0000000000
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/UserSessionClientSessionUpdateTask.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2016 Red Hat, Inc. and/or its affiliates
- * and other contributors as indicated by the @author tags.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.keycloak.models.sessions.infinispan.changes;
-
-import org.jboss.logging.Logger;
-import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
-import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
-
-/**
- * Task for create or update AuthenticatedClientSessionEntity within userSession
- *
- * @author Marek Posolda
- */
-public abstract class UserSessionClientSessionUpdateTask extends UserSessionUpdateTask {
-
- public static final Logger logger = Logger.getLogger(UserSessionClientSessionUpdateTask.class);
-
- private final String clientUUID;
-
- public UserSessionClientSessionUpdateTask(String clientUUID) {
- this.clientUUID = clientUUID;
- }
-
- @Override
- public void runUpdate(UserSessionEntity userSession) {
- AuthenticatedClientSessionEntity clientSession = userSession.getAuthenticatedClientSessions().get(clientUUID);
- if (clientSession == null) {
- logger.warnf("Not found authenticated client session entity for client %s in userSession %s", clientUUID, userSession.getId());
- return;
- }
-
- runClientSessionUpdate(clientSession);
- }
-
- protected abstract void runClientSessionUpdate(AuthenticatedClientSessionEntity entity);
-}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java
index b8a62239d7..898648a7c7 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java
@@ -20,7 +20,6 @@ package org.keycloak.models.sessions.infinispan.entities;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
-import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -29,13 +28,14 @@ import org.infinispan.commons.marshall.Externalizer;
import org.infinispan.commons.marshall.MarshallUtil;
import org.infinispan.commons.marshall.SerializeWith;
import org.keycloak.models.sessions.infinispan.util.KeycloakMarshallUtil;
+import java.util.UUID;
/**
*
* @author Marek Posolda
*/
@SerializeWith(AuthenticatedClientSessionEntity.ExternalizerImpl.class)
-public class AuthenticatedClientSessionEntity implements Serializable {
+public class AuthenticatedClientSessionEntity extends SessionEntity {
private String authMethod;
private String redirectUri;
@@ -49,6 +49,16 @@ public class AuthenticatedClientSessionEntity implements Serializable {
private String currentRefreshToken;
private int currentRefreshTokenUseCount;
+ private final UUID id;
+
+ private AuthenticatedClientSessionEntity(UUID id) {
+ this.id = id;
+ }
+
+ public AuthenticatedClientSessionEntity() {
+ this.id = UUID.randomUUID();
+ }
+
public String getAuthMethod() {
return authMethod;
}
@@ -121,10 +131,16 @@ public class AuthenticatedClientSessionEntity implements Serializable {
this.currentRefreshTokenUseCount = currentRefreshTokenUseCount;
}
+ public UUID getId() {
+ return id;
+ }
+
public static class ExternalizerImpl implements Externalizer {
@Override
public void writeObject(ObjectOutput output, AuthenticatedClientSessionEntity session) throws IOException {
+ MarshallUtil.marshallUUID(session.id, output, false);
+ MarshallUtil.marshallString(session.getRealmId(), output);
MarshallUtil.marshallString(session.getAuthMethod(), output);
MarshallUtil.marshallString(session.getRedirectUri(), output);
MarshallUtil.marshallInt(output, session.getTimestamp());
@@ -143,7 +159,9 @@ public class AuthenticatedClientSessionEntity implements Serializable {
@Override
public AuthenticatedClientSessionEntity readObject(ObjectInput input) throws IOException, ClassNotFoundException {
- AuthenticatedClientSessionEntity sessionEntity = new AuthenticatedClientSessionEntity();
+ AuthenticatedClientSessionEntity sessionEntity = new AuthenticatedClientSessionEntity(MarshallUtil.unmarshallUUID(input, false));
+
+ sessionEntity.setRealmId(MarshallUtil.unmarshallString(input));
sessionEntity.setAuthMethod(MarshallUtil.unmarshallString(input));
sessionEntity.setRedirectUri(MarshallUtil.unmarshallString(input));
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionStore.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionStore.java
new file mode 100644
index 0000000000..ebf946e295
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionStore.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.sessions.infinispan.entities;
+
+import org.keycloak.models.sessions.infinispan.util.KeycloakMarshallUtil;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+import org.infinispan.commons.marshall.Externalizer;
+import org.infinispan.commons.marshall.SerializeWith;
+
+/**
+ *
+ * @author hmlnarik
+ */
+@SerializeWith(AuthenticatedClientSessionStore.ExternalizerImpl.class)
+public class AuthenticatedClientSessionStore {
+
+ /**
+ * Maps client UUID to client session ID.
+ */
+ private final ConcurrentHashMap authenticatedClientSessionIds;
+
+ public AuthenticatedClientSessionStore() {
+ authenticatedClientSessionIds = new ConcurrentHashMap<>();
+ }
+
+ private AuthenticatedClientSessionStore(ConcurrentHashMap authenticatedClientSessionIds) {
+ this.authenticatedClientSessionIds = authenticatedClientSessionIds;
+ }
+
+ public void clear() {
+ authenticatedClientSessionIds.clear();
+ }
+
+ public boolean containsKey(String key) {
+ return authenticatedClientSessionIds.containsKey(key);
+ }
+
+ public void forEach(BiConsumer super String, ? super UUID> action) {
+ authenticatedClientSessionIds.forEach(action);
+ }
+
+ public UUID get(String key) {
+ return authenticatedClientSessionIds.get(key);
+ }
+
+ public Set keySet() {
+ return authenticatedClientSessionIds.keySet();
+ }
+
+ public UUID put(String key, UUID value) {
+ return authenticatedClientSessionIds.put(key, value);
+ }
+
+ public UUID remove(String clientUUID) {
+ return authenticatedClientSessionIds.remove(clientUUID);
+ }
+
+ public int size() {
+ return authenticatedClientSessionIds.size();
+ }
+
+ @Override
+ public String toString() {
+ return this.authenticatedClientSessionIds.toString();
+ }
+
+ public static class ExternalizerImpl implements Externalizer {
+
+ private static final int VERSION_1 = 1;
+
+ @Override
+ public void writeObject(ObjectOutput output, AuthenticatedClientSessionStore obj) throws IOException {
+ output.writeByte(VERSION_1);
+
+ KeycloakMarshallUtil.writeMap(obj.authenticatedClientSessionIds, KeycloakMarshallUtil.STRING_EXT, KeycloakMarshallUtil.UUID_EXT, output);
+ }
+
+ @Override
+ public AuthenticatedClientSessionStore readObject(ObjectInput input) throws IOException, ClassNotFoundException {
+ switch (input.readByte()) {
+ case VERSION_1:
+ return readObjectVersion1(input);
+ default:
+ throw new IOException("Unknown version");
+ }
+ }
+
+ public AuthenticatedClientSessionStore readObjectVersion1(ObjectInput input) throws IOException, ClassNotFoundException {
+ AuthenticatedClientSessionStore res = new AuthenticatedClientSessionStore(
+ KeycloakMarshallUtil.readMap(input, KeycloakMarshallUtil.STRING_EXT, KeycloakMarshallUtil.UUID_EXT, ConcurrentHashMap::new)
+ );
+ return res;
+ }
+ }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java
index 78df451b60..dbde092725 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java
@@ -78,7 +78,7 @@ public class UserSessionEntity extends SessionEntity {
private Map notes = new ConcurrentHashMap<>();
- private Map authenticatedClientSessions = new ConcurrentHashMap<>();
+ private AuthenticatedClientSessionStore authenticatedClientSessions = new AuthenticatedClientSessionStore();
public String getUser() {
return user;
@@ -144,11 +144,11 @@ public class UserSessionEntity extends SessionEntity {
this.notes = notes;
}
- public Map getAuthenticatedClientSessions() {
+ public AuthenticatedClientSessionStore getAuthenticatedClientSessions() {
return authenticatedClientSessions;
}
- public void setAuthenticatedClientSessions(Map authenticatedClientSessions) {
+ public void setAuthenticatedClientSessions(AuthenticatedClientSessionStore authenticatedClientSessions) {
this.authenticatedClientSessions = authenticatedClientSessions;
}
@@ -264,8 +264,7 @@ public class UserSessionEntity extends SessionEntity {
Map notes = session.getNotes();
KeycloakMarshallUtil.writeMap(notes, KeycloakMarshallUtil.STRING_EXT, KeycloakMarshallUtil.STRING_EXT, output);
- Map authSessions = session.getAuthenticatedClientSessions();
- KeycloakMarshallUtil.writeMap(authSessions, KeycloakMarshallUtil.STRING_EXT, new AuthenticatedClientSessionEntity.ExternalizerImpl(), output);
+ output.writeObject(session.getAuthenticatedClientSessions());
}
@@ -285,7 +284,8 @@ public class UserSessionEntity extends SessionEntity {
sessionEntity.setAuthMethod(MarshallUtil.unmarshallString(input));
sessionEntity.setBrokerSessionId(MarshallUtil.unmarshallString(input));
sessionEntity.setBrokerUserId(MarshallUtil.unmarshallString(input));
- sessionEntity.setId(MarshallUtil.unmarshallString(input));
+ final String userSessionId = MarshallUtil.unmarshallString(input);
+ sessionEntity.setId(userSessionId);
sessionEntity.setIpAddress(MarshallUtil.unmarshallString(input));
sessionEntity.setLoginUsername(MarshallUtil.unmarshallString(input));
sessionEntity.setRealmId(MarshallUtil.unmarshallString(input));
@@ -301,8 +301,7 @@ public class UserSessionEntity extends SessionEntity {
new KeycloakMarshallUtil.ConcurrentHashMapBuilder<>());
sessionEntity.setNotes(notes);
- Map authSessions = KeycloakMarshallUtil.readMap(input, KeycloakMarshallUtil.STRING_EXT, new AuthenticatedClientSessionEntity.ExternalizerImpl(),
- new KeycloakMarshallUtil.ConcurrentHashMapBuilder<>());
+ AuthenticatedClientSessionStore authSessions = (AuthenticatedClientSessionStore) input.readObject();
sessionEntity.setAuthenticatedClientSessions(authSessions);
return sessionEntity;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java
index b96b9bd7c8..128a7e98a4 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java
@@ -142,7 +142,7 @@ public class RemoteCacheSessionsLoader implements SessionLoader {
.getAdvancedCache().withFlags(Flag.SKIP_CACHE_LOAD, Flag.SKIP_CACHE_STORE)
.get(OfflinePersistentUserSessionLoader.PERSISTENT_SESSIONS_LOADED_IN_CURRENT_DC);
- if (cacheName.equals(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME) && sessionsLoaded != null && sessionsLoaded) {
+ if (cacheName.equals(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME) && sessionsLoaded != null && sessionsLoaded) {
log.debugf("Sessions already loaded in current DC. Skip sessions loading from remote cache '%s'", cacheName);
return true;
} else {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserSessionPredicate.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserSessionPredicate.java
index d240b13bc5..0b9a764db3 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserSessionPredicate.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserSessionPredicate.java
@@ -17,8 +17,8 @@
package org.keycloak.models.sessions.infinispan.stream;
+import org.keycloak.models.sessions.infinispan.AuthenticatedClientSessionAdapter;
import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
-import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
import org.keycloak.models.sessions.infinispan.util.KeycloakMarshallUtil;
@@ -54,6 +54,11 @@ public class UserSessionPredicate implements Predicate> entry) {
- SessionEntity e = entry.getValue().getEntity();
-
- UserSessionEntity entity = (UserSessionEntity) e;
+ UserSessionEntity entity = entry.getValue().getEntity();
if (!realm.equals(entity.getRealmId())) {
return false;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/KeycloakMarshallUtil.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/KeycloakMarshallUtil.java
index df5c8c673a..a1d83669dc 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/KeycloakMarshallUtil.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/KeycloakMarshallUtil.java
@@ -25,6 +25,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.infinispan.commons.marshall.Externalizer;
@@ -41,7 +42,19 @@ public class KeycloakMarshallUtil {
private static final Logger log = Logger.getLogger(KeycloakMarshallUtil.class);
- public static final StringExternalizer STRING_EXT = new StringExternalizer();
+ public static final Externalizer STRING_EXT = new StringExternalizer();
+
+ public static final Externalizer UUID_EXT = new Externalizer() {
+ @Override
+ public void writeObject(ObjectOutput output, UUID uuid) throws IOException {
+ MarshallUtil.marshallUUID(uuid, output, true);
+ }
+
+ @Override
+ public UUID readObject(ObjectInput input) throws IOException, ClassNotFoundException {
+ return MarshallUtil.unmarshallUUID(input, true);
+ }
+ };
// MAP
diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoteCacheTest.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoteCacheTest.java
index ca06914f58..fb0394a133 100644
--- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoteCacheTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoteCacheTest.java
@@ -77,8 +77,8 @@ public class ConcurrencyJDGRemoteCacheTest {
}
private static Worker createWorker(int threadId) {
- EmbeddedCacheManager manager = new TestCacheManagerFactory().createManager(threadId, InfinispanConnectionProvider.SESSION_CACHE_NAME, RemoteStoreConfigurationBuilder.class);
- Cache cache = manager.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
+ EmbeddedCacheManager manager = new TestCacheManagerFactory().createManager(threadId, InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, RemoteStoreConfigurationBuilder.class);
+ Cache cache = manager.getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
System.out.println("Retrieved cache: " + threadId);
diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java
index 5b7abe0695..536ee33225 100644
--- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java
@@ -41,6 +41,7 @@ import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
import org.keycloak.models.sessions.infinispan.util.InfinispanUtil;
+import java.util.UUID;
/**
* Check that removing of session from remoteCache is session immediately removed on remoteCache in other DC. This is true.
@@ -68,9 +69,11 @@ public class ConcurrencyJDGRemoveSessionTest {
//private static Map state = new HashMap<>();
+ private static final UUID CLIENT_1_UUID = UUID.randomUUID();
+
public static void main(String[] args) throws Exception {
- Cache> cache1 = createManager(1).getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
- Cache> cache2 = createManager(2).getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
+ Cache> cache1 = createManager(1).getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
+ Cache> cache2 = createManager(2).getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
// Create caches, listeners and finally worker threads
Thread worker1 = createWorker(cache1, 1);
@@ -177,7 +180,7 @@ public class ConcurrencyJDGRemoveSessionTest {
clientSession.setTimestamp(1234);
clientSession.setProtocolMappers(new HashSet<>(Arrays.asList("mapper1", "mapper2")));
clientSession.setRoles(new HashSet<>(Arrays.asList("role1", "role2")));
- session.getAuthenticatedClientSessions().put("client1", clientSession);
+ session.getAuthenticatedClientSessions().put(CLIENT_1_UUID.toString(), clientSession.getId());
SessionEntityWrapper wrappedSession = new SessionEntityWrapper<>(session);
return wrappedSession;
@@ -205,7 +208,7 @@ public class ConcurrencyJDGRemoveSessionTest {
private static EmbeddedCacheManager createManager(int threadId) {
- return new TestCacheManagerFactory().createManager(threadId, InfinispanConnectionProvider.SESSION_CACHE_NAME, RemoteStoreConfigurationBuilder.class);
+ return new TestCacheManagerFactory().createManager(threadId, InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, RemoteStoreConfigurationBuilder.class);
}
diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java
index 2b175baca0..2dfb5085d3 100644
--- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java
@@ -36,7 +36,6 @@ import org.infinispan.client.hotrod.event.ClientCacheEntryModifiedEvent;
import org.infinispan.context.Flag;
import org.infinispan.manager.EmbeddedCacheManager;
import org.jboss.logging.Logger;
-import org.junit.Assert;
import org.keycloak.common.util.Time;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
@@ -44,6 +43,7 @@ import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessi
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
import org.keycloak.models.sessions.infinispan.util.InfinispanUtil;
+import java.util.UUID;
import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationBuilder;
/**
@@ -74,9 +74,11 @@ public class ConcurrencyJDGSessionsCacheTest {
//private static Map state = new HashMap<>();
+ private static final UUID CLIENT_1_UUID = UUID.randomUUID();
+
public static void main(String[] args) throws Exception {
- Cache> cache1 = createManager(1).getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
- Cache> cache2 = createManager(2).getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
+ Cache> cache1 = createManager(1).getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
+ Cache> cache2 = createManager(2).getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
// Create initial item
UserSessionEntity session = new UserSessionEntity();
@@ -96,7 +98,7 @@ public class ConcurrencyJDGSessionsCacheTest {
clientSession.setTimestamp(1234);
clientSession.setProtocolMappers(new HashSet<>(Arrays.asList("mapper1", "mapper2")));
clientSession.setRoles(new HashSet<>(Arrays.asList("role1", "role2")));
- session.getAuthenticatedClientSessions().put("client1", clientSession);
+ session.getAuthenticatedClientSessions().put(CLIENT_1_UUID.toString(), clientSession.getId());
SessionEntityWrapper wrappedSession = new SessionEntityWrapper<>(session);
@@ -219,7 +221,7 @@ public class ConcurrencyJDGSessionsCacheTest {
private static EmbeddedCacheManager createManager(int threadId) {
- return new TestCacheManagerFactory().createManager(threadId, InfinispanConnectionProvider.SESSION_CACHE_NAME, RemoteStoreConfigurationBuilder.class);
+ return new TestCacheManagerFactory().createManager(threadId, InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, RemoteStoreConfigurationBuilder.class);
}
diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java
index a5aae29251..a9bb784b2f 100644
--- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java
@@ -38,6 +38,7 @@ import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
+import java.util.UUID;
/**
* Test concurrent writes to distributed cache with usage of atomic replace
@@ -52,6 +53,8 @@ public class DistributedCacheConcurrentWritesTest {
private static final AtomicInteger failedReplaceCounter = new AtomicInteger(0);
private static final AtomicInteger failedReplaceCounter2 = new AtomicInteger(0);
+ private static final UUID CLIENT_1_UUID = UUID.randomUUID();
+
public static void main(String[] args) throws Exception {
CacheWrapper cache1 = createCache("node1");
CacheWrapper cache2 = createCache("node2");
@@ -74,7 +77,7 @@ public class DistributedCacheConcurrentWritesTest {
clientSession.setTimestamp(1234);
clientSession.setProtocolMappers(new HashSet<>(Arrays.asList("mapper1", "mapper2")));
clientSession.setRoles(new HashSet<>(Arrays.asList("role1", "role2")));
- session.getAuthenticatedClientSessions().put("client1", clientSession);
+ session.getAuthenticatedClientSessions().put(CLIENT_1_UUID.toString(), clientSession.getId());
cache1.put("123", session);
@@ -211,7 +214,7 @@ public class DistributedCacheConcurrentWritesTest {
public static CacheWrapper createCache(String nodeName) {
EmbeddedCacheManager mgr = createManager(nodeName);
- Cache> wrapped = mgr.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
+ Cache> wrapped = mgr.getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
return new CacheWrapper<>(wrapped);
}
@@ -245,7 +248,7 @@ public class DistributedCacheConcurrentWritesTest {
}
Configuration distConfig = distConfigBuilder.build();
- cacheManager.defineConfiguration(InfinispanConnectionProvider.SESSION_CACHE_NAME, distConfig);
+ cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, distConfig);
return cacheManager;
}
diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java
index 2ea5245583..d6bc350377 100644
--- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java
@@ -35,27 +35,29 @@ import org.infinispan.transaction.LockingMode;
import org.infinispan.transaction.lookup.DummyTransactionManagerLookup;
import org.infinispan.util.concurrent.IsolationLevel;
import org.jgroups.JChannel;
-import org.junit.Ignore;
import org.keycloak.common.util.Time;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
+import java.util.UUID;
/**
* Test concurrent writes to distributed cache with usage of write skew
*
* @author Marek Posolda
*/
-@Ignore
+//@Ignore
public class DistributedCacheWriteSkewTest {
private static final int ITERATION_PER_WORKER = 1000;
private static final AtomicInteger failedReplaceCounter = new AtomicInteger(0);
+ private static final UUID CLIENT_1_UUID = UUID.randomUUID();
+
public static void main(String[] args) throws Exception {
- Cache cache1 = createManager("node1").getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
- Cache cache2 = createManager("node2").getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
+ Cache cache1 = createManager("node1").getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
+ Cache cache2 = createManager("node2").getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
// Create initial item
UserSessionEntity session = new UserSessionEntity();
@@ -75,7 +77,7 @@ public class DistributedCacheWriteSkewTest {
clientSession.setTimestamp(1234);
clientSession.setProtocolMappers(new HashSet<>(Arrays.asList("mapper1", "mapper2")));
clientSession.setRoles(new HashSet<>(Arrays.asList("role1", "role2")));
- session.getAuthenticatedClientSessions().put("client1", clientSession);
+ session.getAuthenticatedClientSessions().put(CLIENT_1_UUID.toString(), clientSession.getId());
cache1.put("123", session);
@@ -149,6 +151,7 @@ public class DistributedCacheWriteSkewTest {
replaced = true;
} catch (Exception e) {
System.out.println(e);
+ e.printStackTrace();
failedReplaceCounter.incrementAndGet();
}
@@ -208,7 +211,7 @@ public class DistributedCacheWriteSkewTest {
}
Configuration distConfig = distConfigBuilder.build();
- cacheManager.defineConfiguration(InfinispanConnectionProvider.SESSION_CACHE_NAME, distConfig);
+ cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, distConfig);
return cacheManager;
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/PersistentAuthenticatedClientSessionAdapter.java b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentAuthenticatedClientSessionAdapter.java
index 670e9cb3cd..f38b31cec9 100644
--- a/server-spi-private/src/main/java/org/keycloak/models/session/PersistentAuthenticatedClientSessionAdapter.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentAuthenticatedClientSessionAdapter.java
@@ -116,6 +116,10 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
}
@Override
+ public void detachFromUserSession() {
+ setUserSession(null);
+ }
+
public void setUserSession(UserSessionModel userSession) {
this.userSession = userSession;
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java
index e3b577766d..095a85782c 100644
--- a/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java
@@ -26,6 +26,7 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -161,7 +162,7 @@ public class PersistentUserSessionAdapter implements UserSessionModel {
}
@Override
- public void removeAuthenticatedClientSessions(Iterable removedClientUUIDS) {
+ public void removeAuthenticatedClientSessions(Collection removedClientUUIDS) {
if (removedClientUUIDS == null || ! removedClientUUIDS.iterator().hasNext()) {
return;
}
diff --git a/server-spi/src/main/java/org/keycloak/models/AuthenticatedClientSessionModel.java b/server-spi/src/main/java/org/keycloak/models/AuthenticatedClientSessionModel.java
index cee10e18b5..c54533c606 100644
--- a/server-spi/src/main/java/org/keycloak/models/AuthenticatedClientSessionModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/AuthenticatedClientSessionModel.java
@@ -27,7 +27,10 @@ import org.keycloak.sessions.CommonClientSessionModel;
*/
public interface AuthenticatedClientSessionModel extends CommonClientSessionModel {
- void setUserSession(UserSessionModel userSession);
+ /**
+ * Detaches the client session from its user session.
+ */
+ void detachFromUserSession();
UserSessionModel getUserSession();
String getCurrentRefreshToken();
diff --git a/server-spi/src/main/java/org/keycloak/models/UserSessionModel.java b/server-spi/src/main/java/org/keycloak/models/UserSessionModel.java
index ff7c86485c..40fdada880 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserSessionModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserSessionModel.java
@@ -17,6 +17,7 @@
package org.keycloak.models;
+import java.util.Collection;
import java.util.Map;
/**
@@ -53,15 +54,22 @@ public interface UserSessionModel {
void setLastSessionRefresh(int seconds);
/**
- * Returns map where key is ID of the client (its UUID) and value is the respective {@link AuthenticatedClientSessionModel} object.
+ * Returns map where key is ID of the client (its UUID) and value is ID respective {@link AuthenticatedClientSessionModel} object.
* @return
*/
Map getAuthenticatedClientSessions();
+ /**
+ * Returns a client session for the given client UUID.
+ * @return
+ */
+ default AuthenticatedClientSessionModel getAuthenticatedClientSessionByClient(String clientUUID) {
+ return getAuthenticatedClientSessions().get(clientUUID);
+ };
/**
* Removes authenticated client sessions for all clients whose UUID is present in {@code removedClientUUIDS} parameter.
* @param removedClientUUIDS
*/
- void removeAuthenticatedClientSessions(Iterable removedClientUUIDS);
+ void removeAuthenticatedClientSessions(Collection removedClientUUIDS);
public String getNote(String name);
diff --git a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
index 8334838b8c..cd265f2617 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -20,6 +20,7 @@ package org.keycloak.models;
import org.keycloak.provider.Provider;
import java.util.List;
+import java.util.UUID;
import java.util.function.Predicate;
/**
@@ -29,6 +30,7 @@ import java.util.function.Predicate;
public interface UserSessionProvider extends Provider {
AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession);
+ AuthenticatedClientSessionModel getClientSession(UserSessionModel userSession, ClientModel client, UUID clientSessionId, boolean offline);
UserSessionModel createUserSession(String id, RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId);
UserSessionModel getUserSession(RealmModel realm, String id);
@@ -39,7 +41,7 @@ public interface UserSessionProvider extends Provider {
UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId);
/**
- * Return userSession of specified ID as long as the predicate passes. Otherwise returs null.
+ * Return userSession of specified ID as long as the predicate passes. Otherwise returns {@code null}.
* If predicate doesn't pass, implementation can do some best-effort actions to try have predicate passing (eg. download userSession from other DC)
*/
UserSessionModel getUserSessionWithPredicate(RealmModel realm, String id, boolean offline, Predicate predicate);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index 4a0861466c..d0774c48e7 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -159,13 +159,13 @@ public class TokenManager {
}
ClientModel client = session.getContext().getClient();
- AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(client.getId());
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
// Can theoretically happen in cross-dc environment. Try to see if userSession with our client is available in remoteCache
if (clientSession == null) {
userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSession.getId(), offline, client.getId());
if (userSession != null) {
- clientSession = userSession.getAuthenticatedClientSessions().get(client.getId());
+ clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
} else {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session doesn't have required client", "Session doesn't have required client");
}
@@ -400,7 +400,7 @@ public class TokenManager {
public static AuthenticatedClientSessionModel attachAuthenticationSession(KeycloakSession session, UserSessionModel userSession, AuthenticationSessionModel authSession) {
ClientModel client = authSession.getClient();
- AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(client.getId());
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (clientSession == null) {
clientSession = session.sessions().createClientSession(userSession.getRealm(), client, userSession);
}
@@ -436,8 +436,9 @@ public class TokenManager {
return;
}
- clientSession.setUserSession(null);
+ clientSession.detachFromUserSession();
+ // TODO: Might need optimization to prevent loading client sessions from cache in getAuthenticatedClientSessions()
if (userSession.getAuthenticatedClientSessions().isEmpty()) {
sessions.removeUserSession(realm, userSession);
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index cf85aa4e55..562a2082e4 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -254,7 +254,7 @@ public class TokenEndpoint {
// Attempt to use same code twice should invalidate existing clientSession
if (clientSession != null) {
- clientSession.setUserSession(null);
+ clientSession.detachFromUserSession();
}
event.error(Errors.INVALID_CODE);
@@ -400,7 +400,7 @@ public class TokenEndpoint {
if (!result.isOfflineToken()) {
UserSessionModel userSession = session.sessions().getUserSession(realm, res.getSessionState());
- AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(client.getId());
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
updateClientSession(clientSession);
updateUserSessionFromClientAuth(userSession);
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
index 9571fdeeab..0c913d0319 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
@@ -166,7 +166,7 @@ public class UserInfoEndpoint {
// Existence of authenticatedClientSession for our client already handled before
- AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(clientModel.getId());
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(clientModel.getId());
AccessToken userInfo = new AccessToken();
tokenManager.transformUserInfoAccessToken(session, userInfo, realm, clientModel, userModel, userSession, clientSession);
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
index afea781ded..ec2fa26dcb 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -400,7 +400,7 @@ public class SamlService extends AuthorizationEndpointBase {
userSession.setNote(SamlProtocol.SAML_LOGOUT_CANONICALIZATION, samlClient.getCanonicalizationMethod());
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL);
// remove client from logout requests
- AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(client.getId());
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (clientSession != null) {
clientSession.setAction(AuthenticationSessionModel.Action.LOGGED_OUT.name());
}
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlSessionUtils.java b/services/src/main/java/org/keycloak/protocol/saml/SamlSessionUtils.java
index 83b54dde73..f9354b4a1c 100644
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlSessionUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlSessionUtils.java
@@ -62,7 +62,7 @@ public class SamlSessionUtils {
return null;
}
- return userSession.getAuthenticatedClientSessions().get(parts[1]);
+ return userSession.getAuthenticatedClientSessionByClient(clientUUID);
}
}
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 7c779557a4..1e8a53d5fb 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -395,7 +395,7 @@ public class AuthenticationManager {
public static void backchannelLogoutUserFromClient(KeycloakSession session, RealmModel realm, UserModel user, ClientModel client, UriInfo uriInfo, HttpHeaders headers) {
List userSessions = session.sessions().getUserSessions(realm, user);
for (UserSessionModel userSession : userSessions) {
- AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(client.getId());
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (clientSession != null) {
AuthenticationManager.backchannelLogoutClientSession(session, realm, clientSession, null, uriInfo, headers);
clientSession.setAction(AuthenticationSessionModel.Action.LOGGED_OUT.name());
diff --git a/services/src/main/java/org/keycloak/services/managers/CodeGenerateUtil.java b/services/src/main/java/org/keycloak/services/managers/CodeGenerateUtil.java
index 70e3f353cf..de141ff0b0 100644
--- a/services/src/main/java/org/keycloak/services/managers/CodeGenerateUtil.java
+++ b/services/src/main/java/org/keycloak/services/managers/CodeGenerateUtil.java
@@ -172,7 +172,7 @@ class CodeGenerateUtil {
}
}
- return userSession.getAuthenticatedClientSessions().get(codeJWT.getIssuedFor());
+ return userSession.getAuthenticatedClientSessionByClient(codeJWT.getIssuedFor());
}
diff --git a/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java b/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java
index de2516fefc..476022c0cd 100644
--- a/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java
@@ -42,7 +42,7 @@ public class UserSessionCrossDCManager {
// get userSession if it has "authenticatedClientSession" of specified client attached to it. Otherwise download it from remoteCache
public UserSessionModel getUserSessionWithClient(RealmModel realm, String id, boolean offline, String clientUUID) {
- return kcSession.sessions().getUserSessionWithPredicate(realm, id, offline, userSession -> userSession.getAuthenticatedClientSessions().containsKey(clientUUID));
+ return kcSession.sessions().getUserSessionWithPredicate(realm, id, offline, userSession -> userSession.getAuthenticatedClientSessionByClient(clientUUID) != null);
}
@@ -52,8 +52,8 @@ public class UserSessionCrossDCManager {
return kcSession.sessions().getUserSessionWithPredicate(realm, id, false, (UserSessionModel userSession) -> {
- Map authSessions = userSession.getAuthenticatedClientSessions();
- return authSessions.containsKey(clientUUID);
+ AuthenticatedClientSessionModel authSessions = userSession.getAuthenticatedClientSessionByClient(clientUUID);
+ return authSessions != null;
});
}
diff --git a/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java b/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java
index f347d4ce69..1bef11e0fb 100644
--- a/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java
@@ -63,7 +63,7 @@ public class UserSessionManager {
}
// Create and persist clientSession
- AuthenticatedClientSessionModel offlineClientSession = offlineUserSession.getAuthenticatedClientSessions().get(clientSession.getClient().getId());
+ AuthenticatedClientSessionModel offlineClientSession = offlineUserSession.getAuthenticatedClientSessionByClient(clientSession.getClient().getId());
if (offlineClientSession == null) {
createOfflineClientSession(user, clientSession, offlineUserSession);
}
@@ -97,14 +97,14 @@ public class UserSessionManager {
List userSessions = kcSession.sessions().getOfflineUserSessions(realm, user);
boolean anyRemoved = false;
for (UserSessionModel userSession : userSessions) {
- AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(client.getId());
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (clientSession != null) {
if (logger.isTraceEnabled()) {
logger.tracef("Removing existing offline token for user '%s' and client '%s' .",
user.getUsername(), client.getClientId());
}
- clientSession.setUserSession(null);
+ clientSession.detachFromUserSession();
persister.removeClientSession(userSession.getId(), client.getId(), true);
checkOfflineUserSessionHasClientSessions(realm, user, userSession);
anyRemoved = true;
@@ -154,7 +154,8 @@ public class UserSessionManager {
// Check if userSession has any offline clientSessions attached to it. Remove userSession if not
private void checkOfflineUserSessionHasClientSessions(RealmModel realm, UserModel user, UserSessionModel userSession) {
- if (userSession.getAuthenticatedClientSessions().size() > 0) {
+ // TODO: Might need optimization to prevent loading client sessions from cache
+ if (! userSession.getAuthenticatedClientSessions().isEmpty()) {
return;
}
diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java b/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java
index 1f73bc643f..3cfb6d3833 100755
--- a/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java
+++ b/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java
@@ -142,7 +142,7 @@ public class AccountFormService extends AbstractSecuredLocalService {
if (authResult != null) {
UserSessionModel userSession = authResult.getSession();
if (userSession != null) {
- AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(client.getId());
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (clientSession == null) {
clientSession = session.sessions().createClientSession(userSession.getRealm(), client, userSession);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
index 34bbcea9ce..dcfd65115b 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
@@ -339,7 +339,7 @@ public class UserResource {
UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session);
// Update lastSessionRefresh with the timestamp from clientSession
- AuthenticatedClientSessionModel clientSession = session.getAuthenticatedClientSessions().get(clientId);
+ AuthenticatedClientSessionModel clientSession = session.getAuthenticatedClientSessionByClient(clientId);
// Skip if userSession is not for this client
if (clientSession == null) {
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/crossdc/cross-dc-setup.cli b/testsuite/integration-arquillian/servers/auth-server/jboss/common/crossdc/cross-dc-setup.cli
index 3eee2fcc2e..fd086665cf 100644
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/common/crossdc/cross-dc-setup.cli
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/crossdc/cross-dc-setup.cli
@@ -60,6 +60,38 @@ echo ** Update distributed-cache offlineSessions element **
)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions:write-attribute(name=statistics-enabled,value=true)
+echo ** Update distributed-cache clientSessions element **
+/subsystem=infinispan/cache-container=keycloak/distributed-cache=clientSessions/store=remote:add( \
+ passivation=false, \
+ fetch-state=false, \
+ purge=false, \
+ preload=false, \
+ shared=true, \
+ remote-servers=["remote-cache"], \
+ cache=clientSessions, \
+ properties={ \
+ rawValues=true, \
+ marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory \
+ } \
+)
+/subsystem=infinispan/cache-container=keycloak/distributed-cache=clientSessions:write-attribute(name=statistics-enabled,value=true)
+
+echo ** Update distributed-cache offlineClientSessions element **
+/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineClientSessions/store=remote:add( \
+ passivation=false, \
+ fetch-state=false, \
+ purge=false, \
+ preload=false, \
+ shared=true, \
+ remote-servers=["remote-cache"], \
+ cache=offlineClientSessions, \
+ properties={ \
+ rawValues=true, \
+ marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory \
+ } \
+)
+/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineClientSessions:write-attribute(name=statistics-enabled,value=true)
+
echo ** Update distributed-cache loginFailures element **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures/store=remote:add( \
passivation=false, \
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
index d5395f2bbf..697731021e 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
@@ -185,6 +185,7 @@ public class TestingResourceProvider implements RealmResourceProvider {
throw new NotFoundException("Session not found");
}
+ // TODO: Might need optimization to prevent loading client sessions from cache
return sessionModel.getAuthenticatedClientSessions().size();
}
diff --git a/testsuite/integration-arquillian/servers/cache-server/jboss/common/add-keycloak-caches.xsl b/testsuite/integration-arquillian/servers/cache-server/jboss/common/add-keycloak-caches.xsl
index efc0400a0e..8bd5149ea5 100644
--- a/testsuite/integration-arquillian/servers/cache-server/jboss/common/add-keycloak-caches.xsl
+++ b/testsuite/integration-arquillian/servers/cache-server/jboss/common/add-keycloak-caches.xsl
@@ -42,9 +42,10 @@
-
+
+
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/ConcurrentLoginClusterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/ConcurrentLoginClusterTest.java
index 0986c1791d..9b688782d4 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/ConcurrentLoginClusterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/ConcurrentLoginClusterTest.java
@@ -76,7 +76,7 @@ public class ConcurrentLoginClusterTest extends ConcurrentLoginTest {
@Override
public void concurrentLoginSingleUser() throws Throwable {
super.concurrentLoginSingleUser();
- JGroupsStats stats = testingClient.testing().cache(InfinispanConnectionProvider.SESSION_CACHE_NAME).getJgroupsStats();
+ JGroupsStats stats = testingClient.testing().cache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME).getJgroupsStats();
log.info("JGroups statistics: " + stats.statsAsString());
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/AbstractCrossDCTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/AbstractCrossDCTest.java
index 76f6a7e4cc..2c1ca21971 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/AbstractCrossDCTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/AbstractCrossDCTest.java
@@ -388,10 +388,8 @@ public abstract class AbstractCrossDCTest extends AbstractTestRealmKeycloakTest
private void setTimeOffsetOnAllStartedContainers(int offset) {
backendTestingClients.entrySet().stream()
.filter(testingClientEntry -> testingClientEntry.getKey().isStarted())
- .forEach(testingClientEntry -> {
- KeycloakTestingClient testingClient = testingClientEntry.getValue();
- testingClient.testing().setTimeOffset(Collections.singletonMap("offset", String.valueOf(offset)));
- });
+ .map(testingClientEntry -> testingClientEntry.getValue())
+ .forEach(testingClient -> testingClient.testing().setTimeOffset(Collections.singletonMap("offset", String.valueOf(offset))));
}
/**
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/LastSessionRefreshCrossDCTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/LastSessionRefreshCrossDCTest.java
index bf42536372..8f04936f69 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/LastSessionRefreshCrossDCTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/LastSessionRefreshCrossDCTest.java
@@ -26,7 +26,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.Retry;
import org.keycloak.testsuite.arquillian.ContainerInfo;
-import org.keycloak.testsuite.client.KeycloakTestingClient;
import org.keycloak.testsuite.rest.representation.RemoteCacheStats;
import org.keycloak.testsuite.util.OAuthClient;
@@ -57,7 +56,7 @@ public class LastSessionRefreshCrossDCTest extends AbstractAdminCrossDCTest {
// Get statistics
int lsr00 = getTestingClientForStartedNodeInDc(0).testing("test").getLastSessionRefresh("test", sessionId);
int lsr10 = getTestingClientForStartedNodeInDc(1).testing("test").getLastSessionRefresh("test", sessionId);
- int lsrr0 = getTestingClientForStartedNodeInDc(0).testing("test").cache(InfinispanConnectionProvider.SESSION_CACHE_NAME).getRemoteCacheLastSessionRefresh(sessionId);
+ int lsrr0 = getTestingClientForStartedNodeInDc(0).testing("test").cache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME).getRemoteCacheLastSessionRefresh(sessionId);
log.infof("lsr00: %d, lsr10: %d, lsrr0: %d", lsr00, lsr10, lsrr0);
Assert.assertEquals(lsr00, lsr10);
@@ -76,7 +75,7 @@ public class LastSessionRefreshCrossDCTest extends AbstractAdminCrossDCTest {
Retry.execute(() -> {
int lsr01 = getTestingClientForStartedNodeInDc(0).testing("test").getLastSessionRefresh("test", sessionId);
int lsr11 = getTestingClientForStartedNodeInDc(1).testing("test").getLastSessionRefresh("test", sessionId);
- int lsrr1 = getTestingClientForStartedNodeInDc(0).testing("test").cache(InfinispanConnectionProvider.SESSION_CACHE_NAME).getRemoteCacheLastSessionRefresh(sessionId);
+ int lsrr1 = getTestingClientForStartedNodeInDc(0).testing("test").cache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME).getRemoteCacheLastSessionRefresh(sessionId);
log.infof("lsr01: %d, lsr11: %d, lsrr1: %d", lsr01, lsr11, lsrr1);
Assert.assertEquals(lsr01, lsr11);
@@ -88,7 +87,7 @@ public class LastSessionRefreshCrossDCTest extends AbstractAdminCrossDCTest {
disableDcOnLoadBalancer(DC.FIRST);
enableDcOnLoadBalancer(DC.SECOND);
tokenResponse = oauth.doRefreshTokenRequest(refreshToken1, "password");
- Assert.assertNull(tokenResponse.getAccessToken());
+ Assert.assertNull("Expecting no access token present", tokenResponse.getAccessToken());
Assert.assertNotNull(tokenResponse.getError());
// try refresh with new token on DC1. It should pass.
@@ -164,7 +163,7 @@ public class LastSessionRefreshCrossDCTest extends AbstractAdminCrossDCTest {
Assert.assertEquals(lsr10, lsr11.get());
// assert that lastSessionRefresh still the same on remoteCache
- int lsrr1 = getTestingClientForStartedNodeInDc(0).testing("test").cache(InfinispanConnectionProvider.SESSION_CACHE_NAME).getRemoteCacheLastSessionRefresh(sessionId);
+ int lsrr1 = getTestingClientForStartedNodeInDc(0).testing("test").cache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME).getRemoteCacheLastSessionRefresh(sessionId);
Assert.assertEquals(lsr00, lsrr1);
log.infof("lsrr1: %d", lsrr1);
@@ -187,7 +186,7 @@ public class LastSessionRefreshCrossDCTest extends AbstractAdminCrossDCTest {
Assert.assertTrue(lsr02.get() > lsr01.get());
Assert.assertTrue(lsr12.get() > lsr11.get());
- lsrr2.set(getTestingClientForStartedNodeInDc(0).testing("test").cache(InfinispanConnectionProvider.SESSION_CACHE_NAME).getRemoteCacheLastSessionRefresh(sessionId));
+ lsrr2.set(getTestingClientForStartedNodeInDc(0).testing("test").cache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME).getRemoteCacheLastSessionRefresh(sessionId));
log.infof("lsrr2: %d", lsrr2.get());
Assert.assertEquals(lsrr1, lsrr2.get());
@@ -218,7 +217,7 @@ public class LastSessionRefreshCrossDCTest extends AbstractAdminCrossDCTest {
Assert.assertTrue(lsr03.get() > lsr02.get());
Assert.assertTrue(lsr13.get() > lsr12.get());
- lsrr3.set(getTestingClientForStartedNodeInDc(0).testing("test").cache(InfinispanConnectionProvider.SESSION_CACHE_NAME).getRemoteCacheLastSessionRefresh(sessionId));
+ lsrr3.set(getTestingClientForStartedNodeInDc(0).testing("test").cache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME).getRemoteCacheLastSessionRefresh(sessionId));
log.infof("lsrr3: %d", lsrr3.get());
Assert.assertTrue(lsrr3.get() > lsrr2.get());
@@ -232,7 +231,7 @@ public class LastSessionRefreshCrossDCTest extends AbstractAdminCrossDCTest {
private RemoteCacheStats getRemoteCacheStats(int dcIndex) {
return getTestingClientForStartedNodeInDc(dcIndex).testing("test")
- .cache(InfinispanConnectionProvider.SESSION_CACHE_NAME)
+ .cache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME)
.getRemoteCacheStats();
}
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 2644a9af12..1523b33b64 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
@@ -102,10 +102,12 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
@Test
public void testRealmRemoveSessions(
- @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,
+ @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
+ @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
+ @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME) InfinispanStatistics clientCacheDc1Statistics,
+ @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME) InfinispanStatistics clientCacheDc2Statistics,
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
- createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
+ createInitialSessions(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
// log.infof("Sleeping!");
// Thread.sleep(10000000);
@@ -116,7 +118,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
getAdminClient().realm(REALM_NAME).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 realm remove", InfinispanConnectionProvider.SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
+ assertStatisticsExpected("After realm remove", InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
sessions01, sessions02, remoteSessions01, remoteSessions02, 100l);
}
@@ -194,11 +196,11 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
@Test
public void testRealmRemoveOfflineSessions(
- @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,
+ @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
+ @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
- createInitialSessions(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, true, cacheDc1Statistics, cacheDc2Statistics, true);
+ createInitialSessions(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME, true, cacheDc1Statistics, cacheDc2Statistics, true);
channelStatisticsCrossDc.reset();
@@ -206,18 +208,18 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
getAdminClient().realm(REALM_NAME).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 realm remove", InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
+ assertStatisticsExpected("After realm remove", InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
sessions01, sessions02, remoteSessions01, remoteSessions02, 200l); // Might be bigger messages as online sessions removed too.
}
@Test
public void testLogoutAllInRealm(
- @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,
+ @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
+ @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
- createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
+ createInitialSessions(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
channelStatisticsCrossDc.reset();
@@ -225,18 +227,18 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
getAdminClient().realm(REALM_NAME).logoutAll();
// 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 realm logout", InfinispanConnectionProvider.SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
+ assertStatisticsExpected("After realm logout", InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
sessions01, sessions02, remoteSessions01, remoteSessions02, 100l);
}
@Test
public void testPeriodicExpiration(
- @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,
+ @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
+ @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
- OAuthClient.AccessTokenResponse lastAccessTokenResponse = createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true).get(SESSIONS_COUNT - 1);
+ OAuthClient.AccessTokenResponse lastAccessTokenResponse = createInitialSessions(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true).get(SESSIONS_COUNT - 1);
// Assert I am able to refresh
OAuthClient.AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(lastAccessTokenResponse.getRefreshToken(), "password");
@@ -249,7 +251,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
getTestingClientForStartedNodeInDc(0).testing().removeExpired(REALM_NAME);
// Nothing yet expired. Limit 5 for sent_messages is just if "lastSessionRefresh" periodic thread happened
- assertStatisticsExpected("After remove expired - 1", InfinispanConnectionProvider.SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
+ assertStatisticsExpected("After remove expired - 1", InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
sessions01 + SESSIONS_COUNT, sessions02 + SESSIONS_COUNT, remoteSessions01 + SESSIONS_COUNT, remoteSessions02 + SESSIONS_COUNT, 5l);
@@ -268,7 +270,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
getTestingClientForStartedNodeInDc(0).testing().removeExpired(REALM_NAME);
// 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 remove expired - 2", InfinispanConnectionProvider.SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
+ assertStatisticsExpected("After remove expired - 2", InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
sessions01, sessions02, remoteSessions01, remoteSessions02, 100l);
}
@@ -277,10 +279,10 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
@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,
+ @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
+ @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
- createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
+ createInitialSessions(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
// log.infof("Sleeping!");
// Thread.sleep(10000000);
@@ -292,17 +294,17 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
// 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,
+ assertStatisticsExpected("After user remove", InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
sessions01, sessions02, remoteSessions01, remoteSessions02, 100l);
}
@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,
+ @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
+ @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
- createInitialSessions(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, true, cacheDc1Statistics, cacheDc2Statistics, true);
+ createInitialSessions(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME, true, cacheDc1Statistics, cacheDc2Statistics, true);
// log.infof("Sleeping!");
// Thread.sleep(10000000);
@@ -314,18 +316,18 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
// 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,
+ assertStatisticsExpected("After user remove", InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
sessions01, sessions02, remoteSessions01, remoteSessions02, 100l);
}
@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,
+ @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
+ @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
- createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
+ createInitialSessions(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
channelStatisticsCrossDc.reset();
@@ -335,29 +337,29 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
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,
+ assertStatisticsExpected("After logout single session", InfinispanConnectionProvider.USER_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,
+ assertStatisticsExpected("After user logout", InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, cacheDc1Statistics, cacheDc2Statistics, channelStatisticsCrossDc,
sessions01, sessions02, remoteSessions01, remoteSessions02, 100l);
}
@Test
public void testLogoutUserWithFailover(
- @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,
+ @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
+ @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
@JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
// Start node2 on first DC
startBackendNode(DC.FIRST, 1);
// Don't include remote stats. Size is smaller because of distributed cache
- List responses = createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, false);
+ List responses = createInitialSessions(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, false);
// Kill node2 now. Around 10 sessions (half of SESSIONS_COUNT) will be lost on Keycloak side. But not on infinispan side
stopBackendNode(DC.FIRST, 1);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/manual/SessionsPreloadCrossDCTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/manual/SessionsPreloadCrossDCTest.java
index f4a715944d..bc1243b47c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/manual/SessionsPreloadCrossDCTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/manual/SessionsPreloadCrossDCTest.java
@@ -112,7 +112,7 @@ public class SessionsPreloadCrossDCTest extends AbstractAdminCrossDCTest {
@Test
public void sessionsPreloadTest() throws Exception {
- int sessionsBefore = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.SESSION_CACHE_NAME).size();
+ int sessionsBefore = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME).size();
log.infof("sessionsBefore: %d", sessionsBefore);
// Create initial sessions
@@ -124,8 +124,8 @@ public class SessionsPreloadCrossDCTest extends AbstractAdminCrossDCTest {
enableLoadBalancerNode(DC.SECOND, 0);
// Ensure sessions are loaded in both 1st DC and 2nd DC
- int sessions01 = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.SESSION_CACHE_NAME).size();
- int sessions02 = getTestingClientForStartedNodeInDc(1).testing().cache(InfinispanConnectionProvider.SESSION_CACHE_NAME).size();
+ int sessions01 = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME).size();
+ int sessions02 = getTestingClientForStartedNodeInDc(1).testing().cache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME).size();
log.infof("sessions01: %d, sessions02: %d", sessions01, sessions02);
Assert.assertEquals(sessions01, sessionsBefore + SESSIONS_COUNT);
Assert.assertEquals(sessions02, sessionsBefore + SESSIONS_COUNT);
@@ -144,13 +144,13 @@ public class SessionsPreloadCrossDCTest extends AbstractAdminCrossDCTest {
@Test
public void offlineSessionsPreloadTest() throws Exception {
- int offlineSessionsBefore = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME).size();
+ int offlineSessionsBefore = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME).size();
log.infof("offlineSessionsBefore: %d", offlineSessionsBefore);
// Create initial sessions
List tokenResponses = createInitialSessions(true);
- int offlineSessions01 = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME).size();
+ int offlineSessions01 = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME).size();
Assert.assertEquals(offlineSessions01, offlineSessionsBefore + SESSIONS_COUNT);
log.infof("offlineSessions01: %d", offlineSessions01);
@@ -168,8 +168,8 @@ public class SessionsPreloadCrossDCTest extends AbstractAdminCrossDCTest {
enableLoadBalancerNode(DC.SECOND, 0);
// Ensure sessions are loaded in both 1st DC and 2nd DC
- int offlineSessions11 = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME).size();
- int offlineSessions12 = getTestingClientForStartedNodeInDc(1).testing().cache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME).size();
+ int offlineSessions11 = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME).size();
+ int offlineSessions12 = getTestingClientForStartedNodeInDc(1).testing().cache(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME).size();
log.infof("offlineSessions11: %d, offlineSessions12: %d", offlineSessions11, offlineSessions12);
Assert.assertEquals(offlineSessions11, offlineSessionsBefore + SESSIONS_COUNT);
Assert.assertEquals(offlineSessions12, offlineSessionsBefore + SESSIONS_COUNT);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
index 5bbf71ed58..78ec0e7aa0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
@@ -562,7 +562,7 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
RealmModel realmModel = session.getContext().getRealm();
String clientUuid = realmModel.getClientByClientId(clientId).getId();
UserSessionModel userSession = session.sessions().getUserSession(realmModel, sessionId);
- AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(clientUuid);
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(clientUuid);
String claimsInSession = clientSession.getNote(OIDCLoginProtocol.CLAIMS_PARAM);
assertEquals(claimsJson, claimsInSession);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/session/LastSessionRefreshUnitTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/session/LastSessionRefreshUnitTest.java
index 894612725a..569a076e34 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/session/LastSessionRefreshUnitTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/session/LastSessionRefreshUnitTest.java
@@ -169,7 +169,7 @@ public class LastSessionRefreshUnitTest extends AbstractKeycloakTest {
};
- Cache> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
+ Cache> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
return factory.createAndInit(session, cache, timerIntervalMs, maxIntervalBetweenMessagesSeconds, 10, false);
}
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 8c01e2ceaf..9162726b1a 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
@@ -132,7 +132,6 @@ public class UserSessionInitializerTest {
private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set roles, Set protocolMappers) {
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
- if (userSession != null) clientSession.setUserSession(userSession);
clientSession.setRedirectUri(redirect);
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
if (roles != null) clientSession.setRoles(roles);
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
index 8c89046eb2..efa2f7d7ed 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
@@ -362,7 +362,6 @@ public class UserSessionPersisterProviderTest {
private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set roles, Set protocolMappers) {
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
- if (userSession != null) clientSession.setUserSession(userSession);
clientSession.setRedirectUri(redirect);
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
if (roles != null) clientSession.setRoles(roles);
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 106f5258c7..265ecd79da 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
@@ -396,7 +396,6 @@ public class UserSessionProviderOfflineTest {
private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set roles, Set protocolMappers) {
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(client.getRealm(), client, userSession);
- if (userSession != null) clientSession.setUserSession(userSession);
clientSession.setRedirectUri(redirect);
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
if (roles != null) clientSession.setRoles(roles);
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
index e6163315fb..3e2a4dc2fd 100755
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -33,7 +33,6 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.models.UserManager;
-import org.keycloak.sessions.CommonClientSessionModel;
import org.keycloak.testsuite.rule.KeycloakRule;
import java.util.Arrays;
@@ -181,6 +180,30 @@ public class UserSessionProviderTest {
assertEquals(time + 10, updated.getTimestamp());
}
+ @Test
+ public void testUpdateClientSessionWithGetByClientId() {
+ UserSessionModel[] sessions = createSessions();
+
+ String userSessionId = sessions[0].getId();
+ String clientUUID = realm.getClientByClientId("test-app").getId();
+
+ UserSessionModel userSession = session.sessions().getUserSession(realm, userSessionId);
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(clientUUID);
+
+ int time = clientSession.getTimestamp();
+ assertEquals(null, clientSession.getAction());
+
+ clientSession.setAction(AuthenticatedClientSessionModel.Action.LOGGED_OUT.name());
+ clientSession.setTimestamp(time + 10);
+
+ kc.stopSession(session, true);
+ session = kc.startSession();
+
+ AuthenticatedClientSessionModel updated = session.sessions().getUserSession(realm, userSessionId).getAuthenticatedClientSessionByClient(clientUUID);
+ assertEquals(AuthenticatedClientSessionModel.Action.LOGGED_OUT.name(), updated.getAction());
+ assertEquals(time + 10, updated.getTimestamp());
+ }
+
@Test
public void testUpdateClientSessionInSameTransaction() {
UserSessionModel[] sessions = createSessions();
@@ -189,12 +212,12 @@ public class UserSessionProviderTest {
String clientUUID = realm.getClientByClientId("test-app").getId();
UserSessionModel userSession = session.sessions().getUserSession(realm, userSessionId);
- AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(clientUUID);
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(clientUUID);
clientSession.setAction(AuthenticatedClientSessionModel.Action.LOGGED_OUT.name());
clientSession.setNote("foo", "bar");
- AuthenticatedClientSessionModel updated = session.sessions().getUserSession(realm, userSessionId).getAuthenticatedClientSessions().get(clientUUID);
+ AuthenticatedClientSessionModel updated = session.sessions().getUserSession(realm, userSessionId).getAuthenticatedClientSessionByClient(clientUUID);
assertEquals(AuthenticatedClientSessionModel.Action.LOGGED_OUT.name(), updated.getAction());
assertEquals("bar", updated.getNote("foo"));
}
@@ -361,7 +384,6 @@ public class UserSessionProviderTest {
Time.setOffset(i);
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0." + i, "form", false, null, null);
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, realm.getClientByClientId("test-app"), userSession);
- clientSession.setUserSession(userSession);
clientSession.setRedirectUri("http://redirect");
clientSession.setRoles(new HashSet());
clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, "state");
@@ -451,7 +473,7 @@ public class UserSessionProviderTest {
// remove session
clientSession1 = userSession.getAuthenticatedClientSessions().get(client1.getId());
- clientSession1.setUserSession(null);
+ clientSession1.detachFromUserSession();
// Commit and ensure removed
resetSession();
diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/AbstractSessionCacheCommand.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/AbstractSessionCacheCommand.java
index 41495d4060..4790e9cbe7 100644
--- a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/AbstractSessionCacheCommand.java
+++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/AbstractSessionCacheCommand.java
@@ -31,19 +31,28 @@ import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.TreeSet;
/**
* @author Marek Posolda
*/
public abstract class AbstractSessionCacheCommand extends AbstractCommand {
+ private static final Set SUPPORTED_CACHE_NAMES = new TreeSet<>(Arrays.asList(
+ InfinispanConnectionProvider.USER_SESSION_CACHE_NAME,
+ InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME,
+ InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME,
+ InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME
+ ));
+
@Override
protected void doRunCommand(KeycloakSession session) {
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
String cacheName = getArg(0);
- if (!cacheName.equals(InfinispanConnectionProvider.SESSION_CACHE_NAME) && !cacheName.equals(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME)) {
- log.errorf("Invalid cache name: '%s', Only cache names '%s' or '%s' are supported", cacheName, InfinispanConnectionProvider.SESSION_CACHE_NAME,
- InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME);
+ if (! SUPPORTED_CACHE_NAMES.contains(cacheName)) {
+ log.errorf("Invalid cache name: '%s', Only cache names '%s' are supported", cacheName, SUPPORTED_CACHE_NAMES);
throw new HandledException();
}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
index 53e97a5e58..563df2083d 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
@@ -41,7 +41,7 @@ import java.util.List;
public class KeycloakServerDeploymentProcessor implements DeploymentUnitProcessor {
private static final String[] CACHES = new String[] {
- "realms", "users","sessions","authenticationSessions","offlineSessions","loginFailures","work","authorization","keys","actionTokens"
+ "realms", "users","sessions","authenticationSessions","offlineSessions","clientSessions","offlineClientSessions","loginFailures","work","authorization","keys","actionTokens"
};
// This param name is defined again in Keycloak Services class
diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
index 8702948345..b3ea2a9d5f 100755
--- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
+++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
@@ -34,6 +34,8 @@
+
+
@@ -94,6 +96,8 @@
+
+