Send UserRemovedEvent containing all user attributes

Invalidate CachedUserModel before UserRemovedEvent

closes #32194

Signed-off-by: Christian Janker <christian.janker@gmx.at>
This commit is contained in:
Christian Janker 2024-09-13 19:39:34 +02:00 committed by Alexander Schwartz
parent 900c496ffe
commit 21f90145ac
2 changed files with 38 additions and 2 deletions

View file

@ -24,7 +24,6 @@ import org.keycloak.credential.CredentialInput;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.OrganizationModel;
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.component.ComponentModel;
@ -834,6 +833,10 @@ public class UserCacheSession implements UserCache, OnCreateComponent, OnUpdateC
// just in case the transaction is rolled back you need to invalidate the user and all cache queries for that user
protected void fullyInvalidateUser(RealmModel realm, UserModel user) {
if (user instanceof CachedUserModel) {
((CachedUserModel) user).invalidate();
}
Stream<FederatedIdentityModel> federatedIdentities = realm.isIdentityFederationEnabled() ?
getFederatedIdentitiesStream(realm, user) : Stream.empty();
@ -845,7 +848,7 @@ public class UserCacheSession implements UserCache, OnCreateComponent, OnUpdateC
@Override
public boolean removeUser(RealmModel realm, UserModel user) {
fullyInvalidateUser(realm, user);
fullyInvalidateUser(realm, user);
return getDelegate().removeUser(realm, user);
}

View file

@ -35,6 +35,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ResetTimeOffsetEvent;
import org.keycloak.models.utils.SessionTimeoutHelper;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderEventListener;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.arquillian.annotation.ModelTest;
@ -830,6 +831,7 @@ public class UserSessionProviderTest extends AbstractTestRealmKeycloakTest {
public void testOnUserRemoved() {
testingClient.server().run(UserSessionProviderTest::testOnUserRemoved);
}
public static void testOnUserRemoved(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName("test");
session.getContext().setRealm(realm);
@ -857,6 +859,37 @@ public class UserSessionProviderTest extends AbstractTestRealmKeycloakTest {
});
}
@Test
public void testOnUserRemovedLazyUserAttributesAreLoaded() {
testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName("test");
UserModel user1 = session.users().getUserByUsername(realm, "user1");
user1.setSingleAttribute("customAttribute", "value1");
});
testingClient.server().run(UserSessionProviderTest::testOnUserRemovedLazyUserAttributesAreLoaded);
}
public static void testOnUserRemovedLazyUserAttributesAreLoaded(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName("test");
UserModel user1 = session.users().getUserByUsername(realm, "user1");
Map<String, List<String>> attributes = new HashMap<>();
ProviderEventListener providerEventListener = event -> {
if (event instanceof UserModel.UserRemovedEvent) {
UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event;
attributes.putAll(userRemovedEvent.getUser().getAttributes());
}
};
session.getKeycloakSessionFactory().register(providerEventListener);
try {
new UserManager(session).removeUser(realm, user1);
// UserModel.FIRST_NAME, UserModel.LAST_NAME, UserModel.EMAIL, UserModel.USERNAME, customAttribute;
assertEquals(5, attributes.size());
} finally {
session.getKeycloakSessionFactory().unregister(providerEventListener);
}
}
private static AuthenticatedClientSessionModel createClientSession(KeycloakSession session, ClientModel client, UserSessionModel userSession, String redirect, String state) {
RealmModel realm = session.realms().getRealmByName("test");
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);