diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java index 49bcac633b..7f475f5646 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java @@ -140,9 +140,10 @@ public class UserAdapter implements UserModel, JpaModel { if (firstExistingAttrId != null) { // Remove attributes through HQL to avoid StaleUpdateException - Query query = em.createNamedQuery("deleteUserAttributesOtherThan"); - query.setParameter("attrId", firstExistingAttrId); + Query query = em.createNamedQuery("deleteUserAttributesByNameAndUserOtherThan"); + query.setParameter("name", name); query.setParameter("userId", user.getId()); + query.setParameter("attrId", firstExistingAttrId); int numUpdated = query.executeUpdate(); // Remove attribute from local entity diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java index 16b155b75b..5341b647d7 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java @@ -43,7 +43,7 @@ import java.util.Set; @NamedQuery(name="getAttributesByNameAndValue", query="select attr from UserAttributeEntity attr where attr.name = :name and attr.value = :value"), @NamedQuery(name="deleteUserAttributesByRealm", query="delete from UserAttributeEntity attr where attr.user IN (select u from UserEntity u where u.realmId=:realmId)"), @NamedQuery(name="deleteUserAttributesByNameAndUser", query="delete from UserAttributeEntity attr where attr.user.id = :userId and attr.name = :name"), - @NamedQuery(name="deleteUserAttributesOtherThan", query="delete from UserAttributeEntity attr where attr.user.id = :userId and attr.id <> :attrId"), + @NamedQuery(name="deleteUserAttributesByNameAndUserOtherThan", query="delete from UserAttributeEntity attr where attr.user.id = :userId and attr.name = :name and attr.id <> :attrId"), @NamedQuery(name="deleteUserAttributesByRealmAndLink", query="delete from UserAttributeEntity attr where attr.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)") }) @Table(name="USER_ATTRIBUTE") diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java index e2af2416d3..1854d2d922 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java @@ -17,6 +17,7 @@ package org.keycloak.testsuite.model; +import com.google.common.collect.ImmutableMap; import org.junit.Assert; import org.junit.Test; import org.keycloak.models.ClientModel; @@ -216,8 +217,8 @@ public class UserModelTest extends AbstractModelTest { public void testUpdateUserAttribute() throws Exception { RealmModel realm = realmManager.createRealm("original"); UserModel user = session.users().addUser(realm, "user"); - - user.setSingleAttribute("key1", "value1"); + + user.setSingleAttribute("key1", "value1"); commit(); @@ -235,6 +236,30 @@ public class UserModelTest extends AbstractModelTest { commit(); } + + // KEYCLOAK-3608 + @Test + public void testUpdateUserSingleAttribute() { + Map> expected = ImmutableMap.of( + "key1", Arrays.asList("value3"), + "key2", Arrays.asList("value2")); + + RealmModel realm = realmManager.createRealm("original"); + UserModel user = session.users().addUser(realm, "user"); + + user.setSingleAttribute("key1", "value1"); + user.setSingleAttribute("key2", "value2"); + + // Overwrite the first attribute + user.setSingleAttribute("key1", "value3"); + + Assert.assertEquals(expected, user.getAttributes()); + + commit(); + + realm = session.realms().getRealmByName("original"); + Assert.assertEquals(expected, session.users().getUserByUsername("user", realm).getAttributes()); + } @Test public void testSearchByString() {