[SYNCOPE-1102] Minimum Viable Fix
diff --git a/core/src/main/java/org/apache/syncope/core/persistence/beans/membership/MAttr.java b/core/src/main/java/org/apache/syncope/core/persistence/beans/membership/MAttr.java
index 4ebb7f6..204259e 100644
--- a/core/src/main/java/org/apache/syncope/core/persistence/beans/membership/MAttr.java
+++ b/core/src/main/java/org/apache/syncope/core/persistence/beans/membership/MAttr.java
@@ -143,7 +143,7 @@
 
     @Override
     public <T extends AbstractAttrValue> void setUniqueValue(final T uniqueAttributeValue) {
-        if (!(uniqueAttributeValue instanceof MAttrUniqueValue)) {
+        if (uniqueAttributeValue != null && !(uniqueAttributeValue instanceof MAttrUniqueValue)) {
             throw new ClassCastException("uniqueAttributeValue is expected to be typed MAttrUniqueValue: "
                     + uniqueAttributeValue.getClass().getName());
         }
diff --git a/core/src/main/java/org/apache/syncope/core/persistence/beans/role/RAttr.java b/core/src/main/java/org/apache/syncope/core/persistence/beans/role/RAttr.java
index 48d9cd3..e4bf76e 100644
--- a/core/src/main/java/org/apache/syncope/core/persistence/beans/role/RAttr.java
+++ b/core/src/main/java/org/apache/syncope/core/persistence/beans/role/RAttr.java
@@ -140,7 +140,7 @@
 
     @Override
     public <T extends AbstractAttrValue> void setUniqueValue(final T uniqueAttributeValue) {
-        if (!(uniqueAttributeValue instanceof RAttrUniqueValue)) {
+        if (uniqueAttributeValue != null && !(uniqueAttributeValue instanceof RAttrUniqueValue)) {
             throw new ClassCastException("uniqueAttributeValue is expected to be typed RAttrUniqueValue: "
                     + uniqueAttributeValue.getClass().getName());
         }
diff --git a/core/src/main/java/org/apache/syncope/core/persistence/beans/user/UAttr.java b/core/src/main/java/org/apache/syncope/core/persistence/beans/user/UAttr.java
index 7642ba9..b4183ef 100644
--- a/core/src/main/java/org/apache/syncope/core/persistence/beans/user/UAttr.java
+++ b/core/src/main/java/org/apache/syncope/core/persistence/beans/user/UAttr.java
@@ -161,7 +161,7 @@
 
     @Override
     public <T extends AbstractAttrValue> void setUniqueValue(final T uniqueAttributeValue) {
-        if (!(uniqueAttributeValue instanceof UAttrUniqueValue)) {
+        if (uniqueAttributeValue != null && !(uniqueAttributeValue instanceof UAttrUniqueValue)) {
             throw new ClassCastException("uniqueAttributeValue is expected to be typed UAttrUniqueValue: "
                     + uniqueAttributeValue.getClass().getName());
         }
diff --git a/core/src/main/java/org/apache/syncope/core/persistence/dao/impl/AttrValueDAOImpl.java b/core/src/main/java/org/apache/syncope/core/persistence/dao/impl/AttrValueDAOImpl.java
index 4ce43a1..0cf58f4 100644
--- a/core/src/main/java/org/apache/syncope/core/persistence/dao/impl/AttrValueDAOImpl.java
+++ b/core/src/main/java/org/apache/syncope/core/persistence/dao/impl/AttrValueDAOImpl.java
@@ -20,6 +20,7 @@
 
 import java.util.List;
 import javax.persistence.TypedQuery;
+import org.apache.syncope.core.persistence.beans.AbstractAttrUniqueValue;
 import org.apache.syncope.core.persistence.beans.AbstractAttrValue;
 import org.apache.syncope.core.persistence.dao.AttrValueDAO;
 import org.springframework.stereotype.Repository;
@@ -56,7 +57,11 @@
     @Override
     public <T extends AbstractAttrValue> void delete(final T attributeValue) {
         if (attributeValue.getAttribute() != null) {
-            attributeValue.getAttribute().removeValue(attributeValue);
+            if (attributeValue instanceof AbstractAttrUniqueValue) {
+                attributeValue.getAttribute().setUniqueValue(null);
+            } else {
+                attributeValue.getAttribute().removeValue(attributeValue);
+            }
         }
 
         entityManager.remove(attributeValue);
diff --git a/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java b/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java
index f6ed113..6d1e45a 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java
@@ -464,8 +464,10 @@
                         }
                     }
                 }
+                Class<AbstractAttrValue> valueClass =
+                        schema.isUniqueConstraint() ? attrUtil.attrUniqueValueClass() : attrUtil.attrValueClass();
                 for (Long attributeValueId : valuesToBeRemoved) {
-                    attributeValueDAO.delete(attributeValueId, attrUtil.attrValueClass());
+                    attributeValueDAO.delete(attributeValueId, valueClass);
                 }
 
                 // 1.2 add values
diff --git a/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java b/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
index 7619fa4..933ea8e 100644
--- a/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
+++ b/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
@@ -2144,4 +2144,25 @@
         }
         return newMaxId;
     }
+
+    @Test
+    public void issueSYNCOPE1102() {
+        UserTO userTO = getUniqueSampleTO("a@gmail.com");
+        userTO = createUser(userTO);
+        assertNotNull(userTO.getId());
+
+        UserMod userMod = new UserMod();
+        userMod.setId(userTO.getId());
+
+        AttributeMod userIdMod = new AttributeMod();
+        userIdMod.setSchema("userId");
+        userIdMod.getValuesToBeAdded().add("b" + userTO.getUsername());
+        userIdMod.getValuesToBeRemoved().add(userTO.getUsername());
+        userMod.getAttributesToBeUpdated().add(userIdMod);
+
+        userTO = userService.update(userMod.getId(), userMod);
+        assertEquals("b" + userTO.getUsername(), userTO.getAttributeMap().get("userId").getValues().get(0));
+
+        userService.delete(100L);
+    }
 }