You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by an...@apache.org on 2019/05/21 08:00:54 UTC

[syncope] branch 2_0_X updated: [SYNCOPE-1472] fixed multiple entries creation in relationship tables through list checks and constraints on many yo many relationships

This is an automated email from the ASF dual-hosted git repository.

andreapatricelli pushed a commit to branch 2_0_X
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/2_0_X by this push:
     new 1649d6d  [SYNCOPE-1472] fixed multiple entries creation in relationship tables through list checks and constraints on many yo many relationships
1649d6d is described below

commit 1649d6d214ec10fcb9cc4a3e4ab319e873e71d4d
Author: Andrea Patricelli <an...@apache.org>
AuthorDate: Tue May 21 09:45:28 2019 +0200

    [SYNCOPE-1472] fixed multiple entries creation in relationship tables through list checks and constraints on many yo many relationships
---
 .../core/persistence/jpa/entity/JPAAnyType.java    |  7 ++-
 .../core/persistence/jpa/entity/JPARealm.java      |  5 ++-
 .../core/persistence/jpa/entity/JPARole.java       | 13 ++++--
 .../jpa/entity/anyobject/JPAAnyObject.java         | 11 +++--
 .../persistence/jpa/entity/group/JPAGroup.java     | 11 +++--
 .../jpa/entity/group/JPATypeExtension.java         |  3 +-
 .../jpa/entity/policy/JPAAccountPolicy.java        |  5 ++-
 .../jpa/entity/resource/JPAProvision.java          |  4 +-
 .../core/persistence/jpa/entity/user/JPAUser.java  | 14 +++---
 .../apache/syncope/fit/core/AnyObjectITCase.java   | 33 ++++++++++++++
 .../org/apache/syncope/fit/core/AnyTypeITCase.java | 19 ++++++++
 .../org/apache/syncope/fit/core/GroupITCase.java   | 36 +++++++++++++++
 .../org/apache/syncope/fit/core/RealmITCase.java   | 19 ++++++++
 .../org/apache/syncope/fit/core/RoleITCase.java    | 28 ++++++++++++
 .../apache/syncope/fit/core/UserIssuesITCase.java  | 51 ++++++++++++++++++++++
 15 files changed, 235 insertions(+), 24 deletions(-)

diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java
index d9025bc..1af918d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java
@@ -29,6 +29,7 @@ import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
@@ -53,7 +54,9 @@ public class JPAAnyType extends AbstractProvidedKeyEntity implements AnyType {
     @JoinTable(joinColumns =
             @JoinColumn(name = "anyType_id", referencedColumnName = "id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id", referencedColumnName = "id"))
+            @JoinColumn(name = "anyTypeClass_id", referencedColumnName = "id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "anyType_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> classes = new ArrayList<>();
 
     @Override
@@ -69,7 +72,7 @@ public class JPAAnyType extends AbstractProvidedKeyEntity implements AnyType {
     @Override
     public boolean add(final AnyTypeClass anyTypeClass) {
         checkType(anyTypeClass, JPAAnyTypeClass.class);
-        return this.classes.add((JPAAnyTypeClass) anyTypeClass);
+        return classes.contains((JPAAnyTypeClass) anyTypeClass) || this.classes.add((JPAAnyTypeClass) anyTypeClass);
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
index 6feccca..3dced52 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
@@ -92,7 +92,8 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm {
     @JoinTable(joinColumns =
             @JoinColumn(name = "realm_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "resource_id"))
+            @JoinColumn(name = "resource_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "realm_id", "resource_id" }))
     private List<JPAExternalResource> resources = new ArrayList<>();
 
     @Override
@@ -175,7 +176,7 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm {
     @Override
     public boolean add(final ExternalResource resource) {
         checkType(resource, JPAExternalResource.class);
-        return resources.add((JPAExternalResource) resource);
+        return resources.contains((JPAExternalResource) resource) || resources.add((JPAExternalResource) resource);
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
index 30a1f47..4f34981 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
@@ -35,6 +35,7 @@ import javax.persistence.Lob;
 import javax.persistence.ManyToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.Valid;
 import org.apache.syncope.core.persistence.api.entity.user.DynRoleMembership;
 import org.apache.syncope.core.persistence.api.entity.Realm;
@@ -64,7 +65,9 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @JoinTable(joinColumns =
             @JoinColumn(name = "role_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "realm_id"))
+            @JoinColumn(name = "realm_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "role_id", "realm_id" }))
     @Valid
     private List<JPARealm> realms = new ArrayList<>();
 
@@ -72,7 +75,9 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @JoinTable(joinColumns =
             @JoinColumn(name = "role_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "dynamicRealm_id"))
+            @JoinColumn(name = "dynamicRealm_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "role_id", "dynamicRealm_id" }))
     @Valid
     private List<JPADynRealm> dynRealms = new ArrayList<>();
 
@@ -91,7 +96,7 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @Override
     public boolean add(final Realm realm) {
         checkType(realm, JPARealm.class);
-        return realms.add((JPARealm) realm);
+        return realms.contains((JPARealm) realm) || realms.add((JPARealm) realm);
     }
 
     @Override
@@ -102,7 +107,7 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @Override
     public boolean add(final DynRealm dynamicRealm) {
         checkType(dynamicRealm, JPADynRealm.class);
-        return dynRealms.add((JPADynRealm) dynamicRealm);
+        return dynRealms.contains((JPADynRealm) dynamicRealm) || dynRealms.add((JPADynRealm) dynamicRealm);
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java
index 16f94e5..65ad6c4 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java
@@ -31,6 +31,7 @@ import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import org.apache.commons.collections4.IterableUtils;
@@ -76,14 +77,16 @@ public class JPAAnyObject
     @JoinTable(joinColumns =
             @JoinColumn(name = "anyObject_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "resource_id"))
+            @JoinColumn(name = "resource_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "anyObject_id", "resource_id" }))
     private List<JPAExternalResource> resources = new ArrayList<>();
 
     @ManyToMany(fetch = FetchType.LAZY)
     @JoinTable(joinColumns =
             @JoinColumn(name = "anyObject_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id"))
+            @JoinColumn(name = "anyTypeClass_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "anyObject_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd")
@@ -118,7 +121,7 @@ public class JPAAnyObject
     @Override
     public boolean add(final ExternalResource resource) {
         checkType(resource, JPAExternalResource.class);
-        return resources.add((JPAExternalResource) resource);
+        return resources.contains((JPAExternalResource) resource) || resources.add((JPAExternalResource) resource);
     }
 
     @Override
@@ -140,7 +143,7 @@ public class JPAAnyObject
     @Override
     public boolean add(final AnyTypeClass auxClass) {
         checkType(auxClass, JPAAnyTypeClass.class);
-        return this.auxClasses.add((JPAAnyTypeClass) auxClass);
+        return auxClasses.contains((JPAAnyTypeClass) auxClass) || this.auxClasses.add((JPAAnyTypeClass) auxClass);
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
index fc4fc13..8250bc2 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
@@ -32,6 +32,7 @@ import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import org.apache.commons.collections4.IterableUtils;
@@ -83,14 +84,16 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group {
     @JoinTable(joinColumns =
             @JoinColumn(name = "group_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "resource_id"))
+            @JoinColumn(name = "resource_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "group_id", "resource_id" }))
     private List<JPAExternalResource> resources = new ArrayList<>();
 
     @ManyToMany(fetch = FetchType.LAZY)
     @JoinTable(joinColumns =
             @JoinColumn(name = "group_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id"))
+            @JoinColumn(name = "anyTypeClass_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "group_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "group")
@@ -126,7 +129,7 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group {
     @Override
     public boolean add(final ExternalResource resource) {
         checkType(resource, JPAExternalResource.class);
-        return resources.add((JPAExternalResource) resource);
+        return resources.contains((JPAExternalResource) resource) || resources.add((JPAExternalResource) resource);
     }
 
     @Override
@@ -199,7 +202,7 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group {
     @Override
     public boolean add(final AnyTypeClass auxClass) {
         checkType(auxClass, JPAAnyTypeClass.class);
-        return this.auxClasses.add((JPAAnyTypeClass) auxClass);
+        return auxClasses.contains((JPAAnyTypeClass) auxClass) || this.auxClasses.add((JPAAnyTypeClass) auxClass);
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java
index 9ddf68a..9b073d7 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java
@@ -55,7 +55,8 @@ public class JPATypeExtension extends AbstractGeneratedKeyEntity implements Type
     @JoinTable(joinColumns =
             @JoinColumn(name = "typeExtension_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id"))
+            @JoinColumn(name = "anyTypeClass_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "typeExtension_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
index ef0aeb7..a7321e9 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
@@ -31,6 +31,7 @@ import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
 import org.apache.commons.collections4.CollectionUtils;
@@ -66,7 +67,9 @@ public class JPAAccountPolicy extends AbstractPolicy implements AccountPolicy {
     @JoinTable(joinColumns =
             @JoinColumn(name = "accountPolicy_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "resource_id"))
+            @JoinColumn(name = "resource_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "accountPolicy_id", "resource_id" }))
     private Set<JPAExternalResource> resources = new HashSet<>();
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAProvision.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAProvision.java
index 5c10f51..1661340 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAProvision.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAProvision.java
@@ -66,7 +66,9 @@ public class JPAProvision extends AbstractGeneratedKeyEntity implements Provisio
     @JoinTable(joinColumns =
             @JoinColumn(name = "provision_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id"))
+            @JoinColumn(name = "anyTypeClass_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "provision_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @Lob
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
index 6ccf3c7..d543184 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
@@ -42,6 +42,7 @@ import javax.persistence.Table;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 import javax.persistence.Transient;
+import javax.persistence.UniqueConstraint;
 import javax.validation.Valid;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
@@ -93,7 +94,8 @@ public class JPAUser
     @JoinTable(joinColumns =
             @JoinColumn(name = "user_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "role_id"))
+            @JoinColumn(name = "role_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "user_id", "role_id" }))
     private List<JPARole> roles = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
@@ -172,14 +174,16 @@ public class JPAUser
     @JoinTable(joinColumns =
             @JoinColumn(name = "user_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "resource_id"))
+            @JoinColumn(name = "resource_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "user_id", "resource_id" }))
     private List<JPAExternalResource> resources = new ArrayList<>();
 
     @ManyToMany(fetch = FetchType.LAZY)
     @JoinTable(joinColumns =
             @JoinColumn(name = "user_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id"))
+            @JoinColumn(name = "anyTypeClass_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "user_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd")
@@ -209,7 +213,7 @@ public class JPAUser
     @Override
     public boolean add(final ExternalResource resource) {
         checkType(resource, JPAExternalResource.class);
-        return resources.add((JPAExternalResource) resource);
+        return resources.contains((JPAExternalResource) resource) || resources.add((JPAExternalResource) resource);
     }
 
     @Override
@@ -476,7 +480,7 @@ public class JPAUser
     @Override
     public boolean add(final AnyTypeClass auxClass) {
         checkType(auxClass, JPAAnyTypeClass.class);
-        return this.auxClasses.add((JPAAnyTypeClass) auxClass);
+        return auxClasses.contains((JPAAnyTypeClass) auxClass) || this.auxClasses.add((JPAAnyTypeClass) auxClass);
     }
 
     @Override
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
index 9736940..0d53e48 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
@@ -31,6 +31,7 @@ import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.patch.StringPatchItem;
 import org.apache.syncope.common.lib.to.ConnObjectTO;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.AttrTO;
@@ -39,6 +40,7 @@ import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.RelationshipTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.syncope.common.rest.api.beans.AnyQuery;
 import org.apache.syncope.fit.AbstractITCase;
@@ -213,4 +215,35 @@ public class AnyObjectITCase extends AbstractITCase {
             assertEquals(ClientExceptionType.InvalidAnyType, e.getType());
         }
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        // 1. assign resource-db-scripted again to Canon MF 8030cn and update twice
+        AnyObjectPatch anyObjectPatch = new AnyObjectPatch();
+        anyObjectPatch.setKey("8559d14d-58c2-46eb-a2d4-a7d35161e8f8");
+        anyObjectPatch.getResources().add(new StringPatchItem.Builder().value(RESOURCE_NAME_DBSCRIPTED).build());
+        anyObjectPatch.getAuxClasses().add(new StringPatchItem.Builder().value("csv").build());
+
+        for (int i = 0; i < 2; i++) {
+            updateAnyObject(anyObjectPatch);
+        }
+
+        // 2. remove resources and auxiliary classes
+        anyObjectPatch.getResources().clear();
+        anyObjectPatch.getResources().add(new StringPatchItem.Builder()
+                .value(RESOURCE_NAME_DBSCRIPTED)
+                .operation(PatchOperation.DELETE)
+                .build());
+        anyObjectPatch.getAuxClasses().clear();
+        anyObjectPatch.getAuxClasses().add(new StringPatchItem.Builder()
+                .value("csv")
+                .operation(PatchOperation.DELETE)
+                .build());
+
+        updateAnyObject(anyObjectPatch);
+
+        AnyObjectTO printer = anyObjectService.read("8559d14d-58c2-46eb-a2d4-a7d35161e8f8");
+        assertFalse("Should not contain removed resources", printer.getResources().contains(RESOURCE_NAME_DBSCRIPTED));
+        assertFalse("Should not contain removed auxiliary classes", printer.getAuxClasses().contains("csv"));
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java
index a85677d..170dc91 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java
@@ -178,4 +178,23 @@ public class AnyTypeITCase extends AbstractITCase {
             anyTypeService.update(group);
         }
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        // 1. add any type class csv twice to PRINTER any type
+        AnyTypeTO anyTypeTO = anyTypeService.read("PRINTER");
+        anyTypeTO.getClasses().clear();
+        anyTypeTO.getClasses().add("minimal printer");
+        anyTypeTO.getClasses().add("csv");
+        anyTypeTO.getClasses().add("csv");
+        anyTypeService.update(anyTypeTO);
+
+        // 2. read again and remove any type class
+        anyTypeTO = anyTypeService.read("PRINTER");
+        anyTypeTO.getClasses().remove("csv");
+        anyTypeService.update(anyTypeTO);
+
+        assertFalse("Should not contain removed any type classes",
+                anyTypeService.read("PRINTER").getClasses().contains("csv"));
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
index 181af3c..79765eb 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
@@ -1216,4 +1216,40 @@ public class GroupITCase extends AbstractITCase {
             }
         }
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        // 1. update group artDirector by assigning twice resource-testdb and auxiliary class csv
+        GroupPatch groupPatch = new GroupPatch();
+        groupPatch.setKey("ece66293-8f31-4a84-8e8d-23da36e70846");
+        groupPatch.getResources().add(new StringPatchItem.Builder()
+                .value(RESOURCE_NAME_TESTDB)
+                .operation(PatchOperation.ADD_REPLACE)
+                .build());
+        groupPatch.getAuxClasses().add(new StringPatchItem.Builder()
+                .operation(PatchOperation.ADD_REPLACE)
+                .value("csv")
+                .build());
+        for (int i = 0; i < 2; i++) {
+            updateGroup(groupPatch);
+        }
+
+        // 2. remove resources and auxiliary classes
+        groupPatch.getResources().clear();
+        groupPatch.getResources().add(new StringPatchItem.Builder()
+                .value(RESOURCE_NAME_TESTDB)
+                .operation(PatchOperation.DELETE)
+                .build());
+        groupPatch.getAuxClasses().clear();
+        groupPatch.getAuxClasses().add(new StringPatchItem.Builder()
+                .value("csv")
+                .operation(PatchOperation.DELETE)
+                .build());
+
+        updateGroup(groupPatch);
+
+        GroupTO groupTO = groupService.read("ece66293-8f31-4a84-8e8d-23da36e70846");
+        assertFalse("Should not contain removed resources", groupTO.getResources().contains(RESOURCE_NAME_TESTDB));
+        assertFalse("Should not contain removed auxiliary classes", groupTO.getAuxClasses().contains("csv"));
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
index 94fe099..cdddba1 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
@@ -257,4 +257,23 @@ public class RealmITCase extends AbstractITCase {
         assertNull(
                 getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, "ou=test,o=isp"));
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        // 1. assign twice resource-ldap-orgunit to /odd
+        RealmTO realmTO = realmService.list("/odd").get(0);
+        realmTO.getResources().clear();
+        realmTO.getResources().add("resource-ldap-orgunit");
+        realmTO.getResources().add("resource-ldap-orgunit");
+        realmTO = realmService.update(realmTO).readEntity(new GenericType<ProvisioningResult<RealmTO>>() {
+        }).getEntity();
+
+        // 2. remove resource-ldap-orgunit resource
+        realmTO.getResources().remove("resource-ldap-orgunit");
+
+        realmTO = realmService.update(realmTO).readEntity(new GenericType<ProvisioningResult<RealmTO>>() {
+        }).getEntity();
+
+        assertFalse("Should not contain removed resources", realmTO.getResources().contains("resource-ldap-orgunit"));
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java
index fe71fd1..0b4a329 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java
@@ -28,7 +28,9 @@ import java.util.List;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.DynRealmTO;
 import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.common.rest.api.service.RoleService;
@@ -138,4 +140,30 @@ public class RoleITCase extends AbstractITCase {
 
         assertTrue(userService.read("c9b2dec2-00a7-4855-97c0-d854842b4b24").getDynMemberships().isEmpty());
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        final DynRealmTO dynRealmTO = new DynRealmTO();
+        dynRealmTO.setKey("dynRealm");
+        dynRealmTO.getDynMembershipConds().put(AnyTypeKind.USER.name(), "username=~rossini");
+        dynRealmService.create(dynRealmTO);
+
+        // 1. associate role Other again to /odd realm and twice to dynRealm
+        RoleTO roleTO = roleService.read("Other");
+        roleTO.getRealms().add("/odd");
+        roleTO.getDynRealms().add("dynRealm");
+        roleTO.getDynRealms().add("dynRealm");
+        roleService.update(roleTO);
+
+        // 2. update by removing realm and dynamic realm
+        roleTO = roleService.read("Other");
+        roleTO.getRealms().remove("/odd");
+        roleTO.getDynRealms().remove("dynRealm");
+        roleService.update(roleTO);
+
+        roleTO = roleService.read("Other");
+
+        assertFalse("Should not contain removed realms", roleTO.getRealms().contains("/odd"));
+        assertFalse("Should not contain removed dynamic realms", roleTO.getDynRealms().contains("dynRealm"));
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
index 5d7e77e..226b7a7 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
@@ -1484,4 +1484,55 @@ public class UserIssuesITCase extends AbstractITCase {
             configurationService.set(original);
         }
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        // 1. update user rossini by assigning twice resource-testdb2 and auxiliary class csv
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey("1417acbe-cbf6-4277-9372-e75e04f97000");
+        userPatch.setPassword(new PasswordPatch.Builder()
+                .onSyncope(false)
+                .resource(RESOURCE_NAME_TESTDB)
+                .value("Password123")
+                .build());
+        userPatch.getResources().add(new StringPatchItem.Builder()
+                .value(RESOURCE_NAME_TESTDB)
+                .operation(PatchOperation.ADD_REPLACE)
+                .build());
+        userPatch.getAuxClasses().add(new StringPatchItem.Builder()
+                .operation(PatchOperation.ADD_REPLACE)
+                .value("csv")
+                .build());
+        userPatch.getRoles().add(new StringPatchItem.Builder()
+                .operation(PatchOperation.ADD_REPLACE)
+                .value("Other")
+                .build());
+
+        for (int i = 0; i < 2; i++) {
+            updateUser(userPatch);
+        }
+
+        // 2. remove resources, auxiliary classes and roles
+        userPatch.getResources().clear();
+        userPatch.getResources().add(new StringPatchItem.Builder()
+                .value(RESOURCE_NAME_TESTDB)
+                .operation(PatchOperation.DELETE)
+                .build());
+        userPatch.getAuxClasses().clear();
+        userPatch.getAuxClasses().add(new StringPatchItem.Builder()
+                .value("csv")
+                .operation(PatchOperation.DELETE)
+                .build());
+        userPatch.getRoles().clear();
+        userPatch.getRoles().add(new StringPatchItem.Builder()
+                .value("Other")
+                .operation(PatchOperation.DELETE)
+                .build());
+        updateUser(userPatch);
+
+        UserTO userTO = userService.read("1417acbe-cbf6-4277-9372-e75e04f97000");
+        assertFalse("Should not contain removed resources", userTO.getResources().contains(RESOURCE_NAME_TESTDB));
+        assertFalse("Should not contain removed auxiliary classes", userTO.getAuxClasses().contains("csv"));
+        assertFalse("Should not contain removed roles", userTO.getRoles().contains("Other"));
+    }
 }