keycloak-aplcache

Keep setSingleAttribute from deleting all other attributes

9/22/2016 5:42:24 AM

Details

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 16b155b..5341b64 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/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
index 49bcac6..7f475f5 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<UserEntity> {
 
         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/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
index e2af241..1854d2d 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<String, List<String>> 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() {