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 0292efb4ac0b..7d39e80c3bfd 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 @@ -377,13 +377,37 @@ protected Stream getUserSessionsStream(RealmModel realm, UserS @Override public AuthenticatedClientSessionAdapter getClientSession(UserSessionModel userSession, ClientModel client, String clientSessionId, boolean offline) { - return getClientSession(userSession, client, clientSessionId == null ? null : UUID.fromString(clientSessionId), offline); + if (clientSessionId == null) { + return null; + } + + AuthenticatedClientSessionEntity clientSessionEntityFromCache = getClientSessionEntity(UUID.fromString(clientSessionId), offline); + if (clientSessionEntityFromCache != null) { + return wrap(userSession, client, clientSessionEntityFromCache, offline); + } + + if (!offline) { + return null; + } + + log.debugf("Offline client session is not found in cache, try to load from db, userSession [%s] clientSessionId [%s] clientId [%s]", userSession.getId(), clientSessionId, client.getClientId()); + return getClientSessionEntityFromPersistenceProvider(userSession, client, offline); } - @Override - public AuthenticatedClientSessionAdapter getClientSession(UserSessionModel userSession, ClientModel client, UUID clientSessionId, boolean offline) { - AuthenticatedClientSessionEntity entity = getClientSessionEntity(clientSessionId, offline); - return wrap(userSession, client, entity, offline); + private AuthenticatedClientSessionAdapter getClientSessionEntityFromPersistenceProvider(UserSessionModel userSession, ClientModel client, boolean offline) { + if (!(userSession instanceof UserSessionAdapter)) { + log.warnf("User session [%s] is not in cache, should load from db firstly. clientId [%s] offline [%s]", userSession.getId(), client.getClientId(), offline); + return null; + } + + UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class); + AuthenticatedClientSessionModel clientSession = persister.loadClientSessionWithClient(session.getContext().getRealm(), client, userSession, offline); + + if (clientSession == null) { + return null; + } + + return importClientSession((UserSessionAdapter) userSession, clientSession, getTransaction(offline), getClientSessionTransaction(offline), offline); } private AuthenticatedClientSessionEntity getClientSessionEntity(UUID id, boolean offline) { diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/session/JpaUserSessionPersisterProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/session/JpaUserSessionPersisterProvider.java index 03c0fe40da9b..231d942bbbc3 100644 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/session/JpaUserSessionPersisterProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/session/JpaUserSessionPersisterProvider.java @@ -315,6 +315,22 @@ public UserSessionModel loadUserSession(RealmModel realm, String userSessionId, }).orElse(null); } + @Override + public AuthenticatedClientSessionModel loadClientSessionWithClient(RealmModel realm, ClientModel client, UserSessionModel userSession, boolean offline) { + String offlineStr = offlineToString(offline); + + TypedQuery clientSessionQuery = em.createNamedQuery("findClientSessionsByUserSessionWithClient", PersistentClientSessionEntity.class); + clientSessionQuery.setParameter("userSessionId", userSession.getId()); + clientSessionQuery.setParameter("offline", offlineStr); + clientSessionQuery.setParameter("clientId", client.getId()); + clientSessionQuery.setMaxResults(1); + + return closing(clientSessionQuery.getResultStream()) + .map(entity -> toAdapter(realm, client, userSession, entity)) + .findFirst() + .orElse(null); + } + @Override public Stream loadUserSessionsStream(RealmModel realm, ClientModel client, boolean offline, Integer firstResult, Integer maxResults) { @@ -438,10 +454,14 @@ private PersistentAuthenticatedClientSessionAdapter toAdapter(RealmModel realm, } ClientModel client = realm.getClientById(clientId); + return toAdapter(realm, client, userSession, entity); + } + + private PersistentAuthenticatedClientSessionAdapter toAdapter(RealmModel realm, ClientModel client, UserSessionModel userSession, PersistentClientSessionEntity entity) { PersistentClientSessionModel model = new PersistentClientSessionModel(); - model.setClientId(clientId); + model.setClientId(client.getId()); model.setUserSessionId(userSession.getId()); - model.setUserId(userSession.getUserId()); + model.setUserId(userSession.getUser().getId()); model.setTimestamp(entity.getTimestamp()); model.setData(entity.getData()); return new PersistentAuthenticatedClientSessionAdapter(session, model, realm, client, userSession); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/session/PersistentClientSessionEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/session/PersistentClientSessionEntity.java index 6e4648a98c38..a619ba305593 100644 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/session/PersistentClientSessionEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/session/PersistentClientSessionEntity.java @@ -40,6 +40,7 @@ // It is removed from here and added manually in JpaUtils to give a native implementation if needed //@NamedQuery(name="deleteExpiredClientSessions", query="delete from PersistentClientSessionEntity sess where sess.userSessionId IN (select u.userSessionId from PersistentUserSessionEntity u where u.realmId = :realmId AND u.offline = :offline AND u.lastSessionRefresh < :lastSessionRefresh)"), @NamedQuery(name="findClientSessionsByUserSession", query="select sess from PersistentClientSessionEntity sess where sess.userSessionId=:userSessionId and sess.offline = :offline"), + @NamedQuery(name="findClientSessionsByUserSessionWithClient", query="select sess from PersistentClientSessionEntity sess where sess.userSessionId=:userSessionId and sess.offline = :offline and sess.clientId=:clientId"), @NamedQuery(name="findClientSessionsOrderedById", query="select sess from PersistentClientSessionEntity sess where sess.offline = :offline and sess.userSessionId >= :fromSessionId and sess.userSessionId <= :toSessionId order by sess.userSessionId"), @NamedQuery(name="findClientSessionsCountByClient", query="select count(sess) from PersistentClientSessionEntity sess where sess.offline = :offline and sess.clientId = :clientId") }) diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java b/server-spi-private/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java index 7293aaabac4a..0a68458e7b9b 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java @@ -111,10 +111,16 @@ public void removeExpired(RealmModel realm) { } + @Override public UserSessionModel loadUserSession(RealmModel realm, String userSessionId, boolean offline) { return null; } + @Override + public AuthenticatedClientSessionModel loadClientSessionWithClient(RealmModel realm, ClientModel client, UserSessionModel userSession, boolean offline) { + return null; + } + @Override public Stream loadUserSessionsStream(RealmModel realm, ClientModel client, boolean offline, Integer firstResult, Integer maxResults) { return Stream.empty(); diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterProvider.java b/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterProvider.java index 0dac1b9de61a..32fef9681443 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterProvider.java @@ -65,6 +65,16 @@ public interface UserSessionPersisterProvider extends Provider { */ UserSessionModel loadUserSession(RealmModel realm, String userSessionId, boolean offline); + /** + * Loads the user client session with the given @{@link UserSessionModel} and @{@link ClientModel}. + * @param realm + * @param client + * @param userSession + * @param offline + * @return + */ + AuthenticatedClientSessionModel loadClientSessionWithClient(RealmModel realm, ClientModel client, UserSessionModel userSession, boolean offline); + /** * Loads the user sessions for the given {@link UserModel} in the given {@link RealmModel} if present. * @param realm