UserSessionProvider.removeUserSessions now removes all user sessions (both regular and offline)

Closes #31359

Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
This commit is contained in:
Stefan Guilhen 2024-10-25 13:24:37 -03:00 committed by Pedro Igor
parent 3c2e53136b
commit 9861acc2aa
10 changed files with 50 additions and 15 deletions

View File

@ -92,3 +92,9 @@ expect the database schema being updated to add a new column `DETAILS_JSON` to t
The key providers that allow to import externally generated keys (`rsa` and `java-keystore` factories) now check the validity of the associated certificate if present. Therefore a key with a certificate that is expired cannot be imported in {project_name} anymore. If the certificate expires at runtime, the key is converted into a passive key (enabled but not active). A passive key is not used for new tokens, but it is still valid for validating previous issued tokens.
The default `generated` key providers generate a certificate valid for 10 years (the types that have or can have an associated certificate). Because of the long validity and the recommendation to rotate keys frequently, the generated providers do not perform this check.
= Sign out all active sessions in admin console now effectively removes all sessions
In previous versions, clicking on `Sign out all active sessions` in the admin console resulted in the removal of regular sessions only. Offline sessions would still be displayed despite being effectively invalidated.
This has been changed and now all sessions, regular and offline, are removed when signing out of all active sessions.

View File

@ -624,6 +624,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider, Sessi
protected void onRemoveUserSessionsEvent(String realmId) {
removeLocalUserSessions(realmId, false);
removeLocalUserSessions(realmId, true);
}
// public for usage in the testsuite

View File

@ -485,7 +485,7 @@ public class PersistentUserSessionProvider implements UserSessionProvider, Sessi
RemoveUserSessionsEvent.createEvent(RemoveUserSessionsEvent.class, InfinispanUserSessionProviderFactory.REMOVE_USER_SESSIONS_EVENT, session, realm.getId(), true),
ClusterProvider.DCNotify.ALL_DCS);
session.getProvider(UserSessionPersisterProvider.class).removeUserSessions(realm, false);
session.getProvider(UserSessionPersisterProvider.class).removeUserSessions(realm);
}
protected void onRemoveUserSessionsEvent(String realmId) {

View File

@ -195,7 +195,7 @@ public class RemoteUserSessionProvider implements UserSessionProvider {
@Override
public void removeUserSessions(RealmModel realm) {
transaction.removeOnlineSessionsByRealmId(realm.getId());
transaction.removeAllSessionsByRealmId(realm.getId());
}
@Override

View File

@ -191,13 +191,7 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
@Override
public void onRealmRemoved(RealmModel realm) {
em.createNamedQuery("deleteClientSessionsByRealm")
.setParameter("realmId", realm.getId())
.executeUpdate();
em.createNamedQuery("deleteUserSessionsByRealm")
.setParameter("realmId", realm.getId())
.executeUpdate();
this.removeUserSessions(realm);
}
@Override
@ -760,6 +754,15 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
.executeUpdate();
}
@Override
public void removeUserSessions(RealmModel realm) {
em.createNamedQuery("deleteClientSessionsByRealm")
.setParameter("realmId", realm.getId())
.executeUpdate();
em.createNamedQuery("deleteUserSessionsByRealm")
.setParameter("realmId", realm.getId())
.executeUpdate();
}
@Override
public void close() {

View File

@ -147,4 +147,12 @@ public interface UserSessionPersisterProvider extends Provider {
// TODO: remove default implementation
}
/**
* Removes all user sessions from the specified realm.
*/
default void removeUserSessions(RealmModel realm) {
removeUserSessions(realm, true);
removeUserSessions(realm, false);
}
}

View File

@ -151,9 +151,28 @@ public interface UserSessionProvider extends Provider {
*/
void removeExpired(RealmModel realm);
/**
* Removes all user sessions (regular and offline) from the specified realm.
*
* @param realm the realm whose sessions are to be removed.
*/
void removeUserSessions(RealmModel realm);
/**
* Callback method invoked when a realm is removed. Implementations should clear any sessions associated with the removed
* realm.
*
* @param realm a reference to the realm being removed.
*/
void onRealmRemoved(RealmModel realm);
/**
* Callback method invoked when a client is removed. Implementations should clear any sessions associated with the
* removed client.
*
* @param realm a reference to the realm.
* @param client a reference to the client being removed.
*/
void onClientRemoved(RealmModel realm, ClientModel client);
/** Newly created userSession won't contain attached AuthenticatedClientSessions **/

View File

@ -603,7 +603,7 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
setTimeOffset(86400);
// Remove expired sessions. This will remove "normal" userSession
testingClient.testing().removeUserSessions("test");
testingClient.testing().removeExpired("test");
// Refresh with the offline token
tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "secret1");

View File

@ -708,7 +708,7 @@ public class UserInfoTest extends AbstractKeycloakTest {
try {
AccessTokenResponse accessTokenResponse = executeGrantAccessTokenRequest(client, true, true);
testingClient.testing().removeUserSessions("test");
testingClient.testing().removeExpired("test");
Response response = UserInfoClientUtil.executeUserInfoRequest_getMethod(client, accessTokenResponse.getToken());

View File

@ -44,6 +44,7 @@ import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.model.HotRodServerRule;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
@ -93,10 +94,7 @@ public class SessionTimeoutsTest extends KeycloakModelTest {
InfinispanTestUtil.revertTimeService(s);
RealmModel realm = s.realms().getRealm(realmId);
s.getContext().setRealm(realm);
UserModel user1 = s.users().getUserByUsername(realm, "user1");
s.sessions().removeUserSessions(realm);
s.sessions().getOfflineUserSessionsStream(realm, user1).forEach(us -> s.sessions().removeOfflineUserSession(realm, us));
s.realms().removeRealm(realmId);
new RealmManager(s).removeRealm(realm);
super.cleanEnvironment(s);
}