Optimize update of user attributes (#32907)

Closes #32906
Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Alexander Schwartz 2024-09-25 16:39:42 +02:00 committed by GitHub
parent 021a2af2fd
commit 5bb23eb0fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 44 additions and 2 deletions

View file

@ -17,6 +17,7 @@
package org.keycloak.models.cache.infinispan;
import org.keycloak.common.util.CollectionUtil;
import org.keycloak.credential.CredentialModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.GroupModel;
@ -40,6 +41,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@ -174,20 +176,39 @@ public class UserAdapter implements CachedUserModel {
@Override
public void setSingleAttribute(String name, String value) {
getDelegateForUpdate();
if (UserModel.USERNAME.equals(name) || UserModel.EMAIL.equals(name)) {
value = KeycloakModelUtils.toLowerCaseSafe(value);
}
if (updated == null) {
Set<String> oldEntries = getAttributeStream(name).collect(Collectors.toSet());
Set<String> newEntries = value != null ? Set.of(value) : Collections.emptySet();
if (CollectionUtil.collectionEquals(oldEntries, newEntries)) {
return;
}
}
getDelegateForUpdate();
updated.setSingleAttribute(name, value);
}
@Override
public void setAttribute(String name, List<String> values) {
getDelegateForUpdate();
if (UserModel.USERNAME.equals(name) || UserModel.EMAIL.equals(name)) {
String lowerCasedFirstValue = KeycloakModelUtils.toLowerCaseSafe((values != null && values.size() > 0) ? values.get(0) : null);
if (lowerCasedFirstValue != null) values = Collections.singletonList(lowerCasedFirstValue);
}
if (updated == null) {
Set<String> oldEntries = getAttributeStream(name).collect(Collectors.toSet());
Set<String> newEntries;
if (values == null) {
newEntries = new HashSet<>();
} else {
newEntries = new HashSet<>(values);
}
if (CollectionUtil.collectionEquals(oldEntries, newEntries)) {
return;
}
}
getDelegateForUpdate();
updated.setAttribute(name, values);
}

View file

@ -19,6 +19,7 @@ package org.keycloak.models.jpa;
import org.keycloak.common.Profile;
import org.keycloak.common.Profile.Feature;
import org.keycloak.common.util.CollectionUtil;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.ObjectUtil;
import org.keycloak.credential.UserCredentialManager;
@ -51,10 +52,13 @@ import jakarta.persistence.criteria.Root;
import org.keycloak.organization.OrganizationProvider;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.keycloak.representations.idm.MembershipType;
@ -138,6 +142,11 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
if (value == null) {
user.getAttributes().removeIf(a -> a.getName().equals(name));
} else {
Set<String> oldEntries = getAttributeStream(name).collect(Collectors.toSet());
Set<String> newEntries = Set.of(value);
if (CollectionUtil.collectionEquals(oldEntries, newEntries)) {
return;
}
String firstExistingAttrId = null;
List<UserAttributeEntity> toRemove = new ArrayList<>();
for (UserAttributeEntity attr : user.getAttributes()) {
@ -183,6 +192,18 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
setUsername(valueToSet);
return;
}
Set<String> oldEntries = getAttributeStream(name).collect(Collectors.toSet());
Set<String> newEntries;
if (values == null) {
newEntries = new HashSet<>();
} else {
newEntries = new HashSet<>(values);
}
if (CollectionUtil.collectionEquals(oldEntries, newEntries)) {
return;
}
// Remove all existing
removeAttribute(name);
if (values != null) {