You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/08/11 12:52:09 UTC

syncope git commit: [SYNCOPE-119] Adding test for delegated admin (user) CRUD

Repository: syncope
Updated Branches:
  refs/heads/SYNCOPE-652 cebb7de1d -> c853f742f


[SYNCOPE-119] Adding test for delegated admin (user) CRUD


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/c853f742
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/c853f742
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/c853f742

Branch: refs/heads/SYNCOPE-652
Commit: c853f742fbf8cf856c18ace338f438d0e16c551b
Parents: cebb7de
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Aug 11 12:51:57 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Aug 11 12:51:57 2015 +0200

----------------------------------------------------------------------
 .../syncope/common/lib/types/Entitlement.java   |   2 -
 .../syncope/core/logic/AbstractAnyLogic.java    |  25 +++-
 .../syncope/core/logic/AnyObjectLogic.java      | 114 ++++++++++------
 .../apache/syncope/core/logic/GroupLogic.java   | 134 +++++++++++++------
 .../apache/syncope/core/logic/UserLogic.java    | 108 ++++++++++++---
 .../apache/syncope/core/misc/RealmUtils.java    |   4 +-
 .../misc/security/UnauthorizedException.java    |   5 +-
 .../core/persistence/jpa/dao/JPARoleDAO.java    |   9 ++
 .../core/persistence/jpa/outer/RoleTest.java    |  33 +++++
 .../rest/cxf/service/UserSelfServiceImpl.java   |   6 +-
 .../core/reference/AuthenticationITCase.java    |  91 ++++++++++++-
 11 files changed, 426 insertions(+), 105 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/common/lib/src/main/java/org/apache/syncope/common/lib/types/Entitlement.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/Entitlement.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/Entitlement.java
index 85befc6..bf87c89 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/Entitlement.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/Entitlement.java
@@ -108,8 +108,6 @@ public final class Entitlement {
 
     public static final String USER_DELETE = "USER_DELETE";
 
-    public static final String USER_VIEW = "USER_VIEW";
-
     public static final String GROUP_SEARCH = "GROUP_SEARCH";
 
     public static final String GROUP_CREATE = "GROUP_CREATE";

http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
index 6f66113..7a6cfdd 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
@@ -26,7 +26,9 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.syncope.common.lib.mod.AnyMod;
 import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.misc.RealmUtils;
+import org.apache.syncope.core.misc.security.UnauthorizedException;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 
@@ -57,8 +59,8 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, MOD extends AnyMod>
     protected Set<String> getEffectiveRealms(
             final Set<String> allowedRealms, final Collection<String> requestedRealms) {
 
-        final Set<String> allowed = RealmUtils.normalize(allowedRealms);
-        final Set<String> requested = RealmUtils.normalize(requestedRealms);
+        Set<String> allowed = RealmUtils.normalize(allowedRealms);
+        Set<String> requested = RealmUtils.normalize(requestedRealms);
 
         Set<String> effective = new HashSet<>();
         CollectionUtils.select(requested, new StartsWithPredicate(allowed), effective);
@@ -67,6 +69,25 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, MOD extends AnyMod>
         return effective;
     }
 
+    protected void securityChecks(final Set<String> effectiveRealms, final String realm, final Long key) {
+        if (!CollectionUtils.exists(effectiveRealms, new Predicate<String>() {
+
+            @Override
+            public boolean evaluate(final String ownedRealm) {
+                return realm.startsWith(ownedRealm);
+            }
+        })) {
+
+            throw new UnauthorizedException(
+                    this instanceof UserLogic
+                            ? AnyTypeKind.USER
+                            : this instanceof GroupLogic
+                                    ? AnyTypeKind.GROUP
+                                    : AnyTypeKind.ANY_OBJECT,
+                    key);
+        }
+    }
+
     public abstract TO read(Long key);
 
     public abstract int count(List<String> realms);

http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
index 38927ce..0b0cb74 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
@@ -22,6 +22,7 @@ import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -46,9 +47,7 @@ import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.misc.security.AuthContextUtils;
-import org.apache.syncope.core.misc.security.UnauthorizedException;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.provisioning.api.AnyTransformer;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -90,8 +89,8 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_READ + "')")
     @Transactional(readOnly = true)
     @Override
-    public AnyObjectTO read(final Long anyObjectKey) {
-        return binder.getAnyObjectTO(anyObjectKey);
+    public AnyObjectTO read(final Long key) {
+        return binder.getAnyObjectTO(key);
     }
 
     @PreAuthorize("isAuthenticated()")
@@ -164,12 +163,11 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
         if (anyObjectTO.getRealm() == null) {
             throw SyncopeClientException.build(ClientExceptionType.InvalidRealm);
         }
+        // security checks
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_CREATE),
                 Collections.singleton(anyObjectTO.getRealm()));
-        if (effectiveRealms.isEmpty()) {
-            throw new UnauthorizedException(AnyTypeKind.ANY_OBJECT, null);
-        }
+        securityChecks(effectiveRealms, anyObjectTO.getRealm(), null);
 
         // Any transformation (if configured)
         AnyObjectTO actual = attrTransformer.transform(anyObjectTO);
@@ -191,21 +189,25 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
     @Override
     public AnyObjectTO update(final AnyObjectMod anyObjectMod) {
-        AnyObject anyObject = anyObjectDAO.authFind(anyObjectMod.getKey());
-        if (anyObject == null) {
-            throw new NotFoundException("AnyObject with key " + anyObjectMod.getKey());
+        // Any transformation (if configured)
+        AnyObjectMod actual = attrTransformer.transform(anyObjectMod);
+        LOG.debug("Transformed: {}", actual);
+
+        // security checks
+        AnyObjectTO toUpdate = binder.getAnyObjectTO(anyObjectMod.getKey());
+        Set<String> requestedRealms = new HashSet<>();
+        requestedRealms.add(toUpdate.getRealm());
+        if (StringUtils.isNotBlank(actual.getRealm())) {
+            requestedRealms.add(actual.getRealm());
         }
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
-                Collections.singleton(anyObject.getRealm().getFullPath()));
-        if (effectiveRealms.isEmpty()) {
-            throw new UnauthorizedException(AnyTypeKind.ANY_OBJECT, anyObject.getKey());
+                requestedRealms);
+        securityChecks(effectiveRealms, toUpdate.getRealm(), toUpdate.getKey());
+        if (StringUtils.isNotBlank(actual.getRealm())) {
+            securityChecks(effectiveRealms, actual.getRealm(), toUpdate.getKey());
         }
 
-        // Any transformation (if configured)
-        AnyObjectMod actual = attrTransformer.transform(anyObjectMod);
-        LOG.debug("Transformed: {}", actual);
-
         Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(anyObjectMod);
 
         AnyObjectTO updatedTO = binder.getAnyObjectTO(updated.getKey());
@@ -215,22 +217,18 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
 
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_DELETE + "')")
     @Override
-    public AnyObjectTO delete(final Long anyObjectKey) {
-        AnyObject anyObject = anyObjectDAO.authFind(anyObjectKey);
-        if (anyObject == null) {
-            throw new NotFoundException("AnyObject with key " + anyObjectKey);
-        }
+    public AnyObjectTO delete(final Long key) {
+        // security checks
+        AnyObjectTO toDelete = binder.getAnyObjectTO(key);
         Set<String> effectiveRealms = getEffectiveRealms(
-                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
-                Collections.singleton(anyObject.getRealm().getFullPath()));
-        if (effectiveRealms.isEmpty()) {
-            throw new UnauthorizedException(AnyTypeKind.ANY_OBJECT, anyObject.getKey());
-        }
+                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_DELETE),
+                Collections.singleton(toDelete.getRealm()));
+        securityChecks(effectiveRealms, toDelete.getRealm(), toDelete.getKey());
 
-        List<PropagationStatus> statuses = provisioningManager.delete(anyObjectKey);
+        List<PropagationStatus> statuses = provisioningManager.delete(key);
 
         AnyObjectTO anyObjectTO = new AnyObjectTO();
-        anyObjectTO.setKey(anyObjectKey);
+        anyObjectTO.setKey(key);
 
         anyObjectTO.getPropagationStatusTOs().addAll(statuses);
 
@@ -239,9 +237,16 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
 
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
     @Override
-    public AnyObjectTO unlink(final Long anyObjectKey, final Collection<String> resources) {
+    public AnyObjectTO unlink(final Long key, final Collection<String> resources) {
+        // security checks
+        AnyObjectTO anyObject = binder.getAnyObjectTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
+                Collections.singleton(anyObject.getRealm()));
+        securityChecks(effectiveRealms, anyObject.getRealm(), anyObject.getKey());
+
         AnyObjectMod anyObjectMod = new AnyObjectMod();
-        anyObjectMod.setKey(anyObjectKey);
+        anyObjectMod.setKey(key);
         anyObjectMod.getResourcesToRemove().addAll(resources);
 
         return binder.getAnyObjectTO(provisioningManager.unlink(anyObjectMod));
@@ -249,9 +254,16 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
 
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
     @Override
-    public AnyObjectTO link(final Long anyObjectKey, final Collection<String> resources) {
+    public AnyObjectTO link(final Long key, final Collection<String> resources) {
+        // security checks
+        AnyObjectTO anyObject = binder.getAnyObjectTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
+                Collections.singleton(anyObject.getRealm()));
+        securityChecks(effectiveRealms, anyObject.getRealm(), anyObject.getKey());
+
         AnyObjectMod anyObjectMod = new AnyObjectMod();
-        anyObjectMod.setKey(anyObjectKey);
+        anyObjectMod.setKey(key);
         anyObjectMod.getResourcesToAdd().addAll(resources);
 
         return binder.getAnyObjectTO(provisioningManager.link(anyObjectMod));
@@ -259,9 +271,16 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
 
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
     @Override
-    public AnyObjectTO unassign(final Long anyObjectKey, final Collection<String> resources) {
+    public AnyObjectTO unassign(final Long key, final Collection<String> resources) {
+        // security checks
+        AnyObjectTO anyObject = binder.getAnyObjectTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
+                Collections.singleton(anyObject.getRealm()));
+        securityChecks(effectiveRealms, anyObject.getRealm(), anyObject.getKey());
+
         AnyObjectMod anyObjectMod = new AnyObjectMod();
-        anyObjectMod.setKey(anyObjectKey);
+        anyObjectMod.setKey(key);
         anyObjectMod.getResourcesToRemove().addAll(resources);
         return update(anyObjectMod);
     }
@@ -274,6 +293,13 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
             final boolean changepwd,
             final String password) {
 
+        // security checks
+        AnyObjectTO anyObject = binder.getAnyObjectTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
+                Collections.singleton(anyObject.getRealm()));
+        securityChecks(effectiveRealms, anyObject.getRealm(), anyObject.getKey());
+
         AnyObjectMod anyObjectMod = new AnyObjectMod();
         anyObjectMod.setKey(key);
         anyObjectMod.getResourcesToAdd().addAll(resources);
@@ -284,6 +310,13 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
     @Override
     public AnyObjectTO deprovision(final Long key, final Collection<String> resources) {
+        // security checks
+        AnyObjectTO anyObject = binder.getAnyObjectTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
+                Collections.singleton(anyObject.getRealm()));
+        securityChecks(effectiveRealms, anyObject.getRealm(), anyObject.getKey());
+
         List<PropagationStatus> statuses = provisioningManager.deprovision(key, resources);
 
         AnyObjectTO updatedTO = binder.getAnyObjectTO(key);
@@ -299,10 +332,15 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
             final boolean changePwd,
             final String password) {
 
-        AnyObjectTO original = binder.getAnyObjectTO(key);
-        original.getPropagationStatusTOs().addAll(provisioningManager.provision(key, resources));
+        // security checks
+        AnyObjectTO anyObject = binder.getAnyObjectTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
+                Collections.singleton(anyObject.getRealm()));
+        securityChecks(effectiveRealms, anyObject.getRealm(), anyObject.getKey());
 
-        return original;
+        anyObject.getPropagationStatusTOs().addAll(provisioningManager.provision(key, resources));
+        return anyObject;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
index 33b0447..1c3d7f5 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
@@ -22,13 +22,16 @@ import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.annotation.Resource;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.mod.GroupMod;
@@ -50,7 +53,6 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.misc.security.AuthContextUtils;
 import org.apache.syncope.core.misc.security.UnauthorizedException;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.provisioning.api.AnyTransformer;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -91,11 +93,25 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
     @Autowired
     protected GroupProvisioningManager provisioningManager;
 
+    @Override
+    protected void securityChecks(final Set<String> effectiveRealms, final String realm, final Long key) {
+        if (!CollectionUtils.exists(effectiveRealms, new Predicate<String>() {
+
+            @Override
+            public boolean evaluate(final String ownedRealm) {
+                return realm.startsWith(ownedRealm) || ownedRealm.equals(RealmUtils.getGroupOwnerRealm(realm, key));
+            }
+        })) {
+
+            throw new UnauthorizedException(AnyTypeKind.GROUP, key);
+        }
+    }
+
     @PreAuthorize("hasRole('" + Entitlement.GROUP_READ + "')")
     @Transactional(readOnly = true)
     @Override
-    public GroupTO read(final Long groupKey) {
-        return binder.getGroupTO(groupKey);
+    public GroupTO read(final Long key) {
+        return binder.getGroupTO(key);
     }
 
     @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
@@ -153,7 +169,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
     public List<GroupTO> search(final SearchCond searchCondition, final int page, final int size,
             final List<OrderByClause> orderBy, final List<String> realms, final boolean details) {
 
-        final List<Group> matchingGroups = searchDAO.search(
+        List<Group> matchingGroups = searchDAO.search(
                 getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_SEARCH), realms),
                 searchCondition, page, size, orderBy, AnyTypeKind.GROUP);
         return CollectionUtils.collect(matchingGroups, new Transformer<Group, GroupTO>() {
@@ -169,15 +185,13 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
     @Override
     public GroupTO create(final GroupTO groupTO) {
         if (groupTO.getRealm() == null) {
-            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
-            throw sce;
+            throw SyncopeClientException.build(ClientExceptionType.InvalidRealm);
         }
+        // security checks
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_CREATE),
                 Collections.singleton(groupTO.getRealm()));
-        if (effectiveRealms.isEmpty()) {
-            throw new UnauthorizedException(AnyTypeKind.GROUP, null);
-        }
+        securityChecks(effectiveRealms, groupTO.getRealm(), null);
 
         // Any transformation (if configured)
         GroupTO actual = attrTransformer.transform(groupTO);
@@ -195,21 +209,25 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
     @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
     @Override
     public GroupTO update(final GroupMod groupMod) {
-        Group group = groupDAO.authFind(groupMod.getKey());
-        if (group == null) {
-            throw new NotFoundException("Group with key " + groupMod.getKey());
+        // Any transformation (if configured)
+        GroupMod actual = attrTransformer.transform(groupMod);
+        LOG.debug("Transformed: {}", actual);
+
+        // security checks
+        GroupTO toUpdate = binder.getGroupTO(groupMod.getKey());
+        Set<String> requestedRealms = new HashSet<>();
+        requestedRealms.add(toUpdate.getRealm());
+        if (StringUtils.isNotBlank(actual.getRealm())) {
+            requestedRealms.add(actual.getRealm());
         }
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_UPDATE),
-                Collections.singleton(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())));
-        if (effectiveRealms.isEmpty()) {
-            throw new UnauthorizedException(AnyTypeKind.GROUP, group.getKey());
+                requestedRealms);
+        securityChecks(effectiveRealms, toUpdate.getRealm(), toUpdate.getKey());
+        if (StringUtils.isNotBlank(actual.getRealm())) {
+            securityChecks(effectiveRealms, actual.getRealm(), toUpdate.getKey());
         }
 
-        // Any transformation (if configured)
-        GroupMod actual = attrTransformer.transform(groupMod);
-        LOG.debug("Transformed: {}", actual);
-
         Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(groupMod);
 
         GroupTO updatedTO = binder.getGroupTO(updated.getKey());
@@ -219,19 +237,15 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
 
     @PreAuthorize("hasRole('" + Entitlement.GROUP_DELETE + "')")
     @Override
-    public GroupTO delete(final Long groupKey) {
-        Group group = groupDAO.authFind(groupKey);
-        if (group == null) {
-            throw new NotFoundException("Group with key " + groupKey);
-        }
+    public GroupTO delete(final Long key) {
+        // security checks
+        GroupTO toDelete = binder.getGroupTO(key);
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_DELETE),
-                Collections.singleton(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())));
-        if (effectiveRealms.isEmpty()) {
-            throw new UnauthorizedException(AnyTypeKind.GROUP, group.getKey());
-        }
+                Collections.singleton(toDelete.getRealm()));
+        securityChecks(effectiveRealms, toDelete.getRealm(), toDelete.getKey());
 
-        List<Group> ownedGroups = groupDAO.findOwnedByGroup(groupKey);
+        List<Group> ownedGroups = groupDAO.findOwnedByGroup(key);
         if (!ownedGroups.isEmpty()) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
             sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() {
@@ -244,10 +258,10 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
             throw sce;
         }
 
-        List<PropagationStatus> statuses = provisioningManager.delete(groupKey);
+        List<PropagationStatus> statuses = provisioningManager.delete(key);
 
         GroupTO groupTO = new GroupTO();
-        groupTO.setKey(groupKey);
+        groupTO.setKey(key);
 
         groupTO.getPropagationStatusTOs().addAll(statuses);
 
@@ -256,9 +270,16 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
 
     @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
     @Override
-    public GroupTO unlink(final Long groupKey, final Collection<String> resources) {
+    public GroupTO unlink(final Long key, final Collection<String> resources) {
+        // security checks
+        GroupTO group = binder.getGroupTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_UPDATE),
+                Collections.singleton(group.getRealm()));
+        securityChecks(effectiveRealms, group.getRealm(), group.getKey());
+
         GroupMod groupMod = new GroupMod();
-        groupMod.setKey(groupKey);
+        groupMod.setKey(key);
         groupMod.getResourcesToRemove().addAll(resources);
 
         return binder.getGroupTO(provisioningManager.unlink(groupMod));
@@ -266,9 +287,16 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
 
     @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
     @Override
-    public GroupTO link(final Long groupKey, final Collection<String> resources) {
+    public GroupTO link(final Long key, final Collection<String> resources) {
+        // security checks
+        GroupTO group = binder.getGroupTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_UPDATE),
+                Collections.singleton(group.getRealm()));
+        securityChecks(effectiveRealms, group.getRealm(), group.getKey());
+
         GroupMod groupMod = new GroupMod();
-        groupMod.setKey(groupKey);
+        groupMod.setKey(key);
         groupMod.getResourcesToAdd().addAll(resources);
 
         return binder.getGroupTO(provisioningManager.link(groupMod));
@@ -276,9 +304,16 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
 
     @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
     @Override
-    public GroupTO unassign(final Long groupKey, final Collection<String> resources) {
+    public GroupTO unassign(final Long key, final Collection<String> resources) {
+        // security checks
+        GroupTO group = binder.getGroupTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_UPDATE),
+                Collections.singleton(group.getRealm()));
+        securityChecks(effectiveRealms, group.getRealm(), group.getKey());
+
         GroupMod groupMod = new GroupMod();
-        groupMod.setKey(groupKey);
+        groupMod.setKey(key);
         groupMod.getResourcesToRemove().addAll(resources);
         return update(groupMod);
     }
@@ -291,6 +326,13 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
             final boolean changepwd,
             final String password) {
 
+        // security checks
+        GroupTO group = binder.getGroupTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_UPDATE),
+                Collections.singleton(group.getRealm()));
+        securityChecks(effectiveRealms, group.getRealm(), group.getKey());
+
         GroupMod groupMod = new GroupMod();
         groupMod.setKey(key);
         groupMod.getResourcesToAdd().addAll(resources);
@@ -301,6 +343,13 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
     @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
     @Override
     public GroupTO deprovision(final Long key, final Collection<String> resources) {
+        // security checks
+        GroupTO group = binder.getGroupTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_UPDATE),
+                Collections.singleton(group.getRealm()));
+        securityChecks(effectiveRealms, group.getRealm(), group.getKey());
+
         List<PropagationStatus> statuses = provisioningManager.deprovision(key, resources);
 
         GroupTO updatedTO = binder.getGroupTO(key);
@@ -316,10 +365,15 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
             final boolean changePwd,
             final String password) {
 
-        GroupTO original = binder.getGroupTO(key);
-        original.getPropagationStatusTOs().addAll(provisioningManager.provision(key, resources));
+        // security checks
+        GroupTO group = binder.getGroupTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_UPDATE),
+                Collections.singleton(group.getRealm()));
+        securityChecks(effectiveRealms, group.getRealm(), group.getKey());
 
-        return original;
+        group.getPropagationStatusTOs().addAll(provisioningManager.provision(key, resources));
+        return group;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
index 28e7401..ff87d6b 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
@@ -23,12 +23,14 @@ import java.security.AccessControlException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -51,7 +53,6 @@ import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.misc.security.AuthContextUtils;
-import org.apache.syncope.core.misc.security.UnauthorizedException;
 import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.provisioning.api.AnyTransformer;
@@ -180,7 +181,7 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
     }
 
     @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
-    public UserTO createSelf(final UserTO userTO, final boolean storePassword) {
+    public UserTO selfCreate(final UserTO userTO, final boolean storePassword) {
         return doCreate(userTO, storePassword);
     }
 
@@ -193,15 +194,13 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
     @PreAuthorize("hasRole('" + Entitlement.USER_CREATE + "')")
     public UserTO create(final UserTO userTO, final boolean storePassword) {
         if (userTO.getRealm() == null) {
-            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
-            throw sce;
+            throw SyncopeClientException.build(ClientExceptionType.InvalidRealm);
         }
+        // security checks
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.USER_CREATE),
                 Collections.singleton(userTO.getRealm()));
-        if (effectiveRealms.isEmpty()) {
-            throw new UnauthorizedException(AnyTypeKind.USER, null);
-        }
+        securityChecks(effectiveRealms, userTO.getRealm(), null);
 
         return doCreate(userTO, storePassword);
     }
@@ -219,14 +218,14 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
     }
 
     @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
-    public UserTO updateSelf(final UserMod userMod) {
+    public UserTO selfUpdate(final UserMod userMod) {
         UserTO userTO = binder.getAuthenticatedUserTO();
 
         if (userTO.getKey() != userMod.getKey()) {
             throw new AccessControlException("Not allowed for user with key " + userMod.getKey());
         }
 
-        return update(userMod);
+        return doUpdate(userMod);
     }
 
     @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
@@ -236,7 +235,26 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
         UserMod actual = anyTransformer.transform(userMod);
         LOG.debug("Transformed: {}", actual);
 
-        Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(actual);
+        // security checks
+        UserTO toUpdate = binder.getUserTO(userMod.getKey());
+        Set<String> requestedRealms = new HashSet<>();
+        requestedRealms.add(toUpdate.getRealm());
+        if (StringUtils.isNotBlank(actual.getRealm())) {
+            requestedRealms.add(actual.getRealm());
+        }
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
+                requestedRealms);
+        securityChecks(effectiveRealms, toUpdate.getRealm(), toUpdate.getKey());
+        if (StringUtils.isNotBlank(actual.getRealm())) {
+            securityChecks(effectiveRealms, actual.getRealm(), toUpdate.getKey());
+        }
+
+        return doUpdate(actual);
+    }
+
+    protected UserTO doUpdate(final UserMod userMod) {
+        Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(userMod);
 
         UserTO updatedTO = binder.getUserTO(updated.getKey());
         updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
@@ -267,8 +285,15 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
 
     @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
     public UserTO status(final StatusMod statusMod) {
+        // security checks
+        UserTO toUpdate = binder.getUserTO(statusMod.getKey());
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
+                Collections.singleton(toUpdate.getRealm()));
+        securityChecks(effectiveRealms, toUpdate.getRealm(), toUpdate.getKey());
+
         Map.Entry<Long, List<PropagationStatus>> updated = setStatusOnWfAdapter(statusMod);
-        final UserTO savedTO = binder.getUserTO(updated.getKey());
+        UserTO savedTO = binder.getUserTO(updated.getKey());
         savedTO.getPropagationStatusTOs().addAll(updated.getValue());
         return savedTO;
     }
@@ -305,15 +330,26 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
     }
 
     @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
-    public UserTO deleteSelf() {
+    public UserTO selfDelete() {
         UserTO userTO = binder.getAuthenticatedUserTO();
 
-        return delete(userTO.getKey());
+        return doDelete(userTO.getKey());
     }
 
     @PreAuthorize("hasRole('" + Entitlement.USER_DELETE + "')")
     @Override
     public UserTO delete(final Long key) {
+        // security checks
+        UserTO toDelete = binder.getUserTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.USER_DELETE),
+                Collections.singleton(toDelete.getRealm()));
+        securityChecks(effectiveRealms, toDelete.getRealm(), toDelete.getKey());
+
+        return doDelete(key);
+    }
+
+    protected UserTO doDelete(final Long key) {
         List<Group> ownedGroups = groupDAO.findOwnedByUser(key);
         if (!ownedGroups.isEmpty()) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
@@ -344,6 +380,13 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
     @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
     @Override
     public UserTO unlink(final Long key, final Collection<String> resources) {
+        // security checks
+        UserTO user = binder.getUserTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
+                Collections.singleton(user.getRealm()));
+        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
+
         UserMod userMod = new UserMod();
         userMod.setKey(key);
         userMod.getResourcesToRemove().addAll(resources);
@@ -354,6 +397,13 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
     @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
     @Override
     public UserTO link(final Long key, final Collection<String> resources) {
+        // security checks
+        UserTO user = binder.getUserTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
+                Collections.singleton(user.getRealm()));
+        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
+
         UserMod userMod = new UserMod();
         userMod.setKey(key);
         userMod.getResourcesToAdd().addAll(resources);
@@ -364,6 +414,13 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
     @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
     @Override
     public UserTO unassign(final Long key, final Collection<String> resources) {
+        // security checks
+        UserTO user = binder.getUserTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
+                Collections.singleton(user.getRealm()));
+        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
+
         UserMod userMod = new UserMod();
         userMod.setKey(key);
         userMod.getResourcesToRemove().addAll(resources);
@@ -378,6 +435,13 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
             final boolean changepwd,
             final String password) {
 
+        // security checks
+        UserTO user = binder.getUserTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
+                Collections.singleton(user.getRealm()));
+        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
+
         UserMod userMod = new UserMod();
         userMod.setKey(key);
         userMod.getResourcesToAdd().addAll(resources);
@@ -396,6 +460,13 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
     @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
     @Override
     public UserTO deprovision(final Long key, final Collection<String> resources) {
+        // security checks
+        UserTO user = binder.getUserTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
+                Collections.singleton(user.getRealm()));
+        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
+
         List<PropagationStatus> statuses = provisioningManager.deprovision(key, resources);
 
         UserTO updatedTO = binder.getUserTO(key);
@@ -411,10 +482,15 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
             final boolean changePwd,
             final String password) {
 
-        UserTO original = binder.getUserTO(key);
-        original.getPropagationStatusTOs().addAll(provisioningManager.provision(key, changePwd, password, resources));
+        // security checks
+        UserTO user = binder.getUserTO(key);
+        Set<String> effectiveRealms = getEffectiveRealms(
+                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
+                Collections.singleton(user.getRealm()));
+        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
 
-        return original;
+        user.getPropagationStatusTOs().addAll(provisioningManager.provision(key, changePwd, password, resources));
+        return user;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/core/misc/src/main/java/org/apache/syncope/core/misc/RealmUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/RealmUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/RealmUtils.java
index 7a921ee..d8bacdb 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/RealmUtils.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/RealmUtils.java
@@ -24,8 +24,8 @@ import java.util.Set;
 
 public final class RealmUtils {
 
-    public static String getGroupOwnerRealm(final String realmPath, final Long groupId) {
-        return realmPath + "@" + groupId;
+    public static String getGroupOwnerRealm(final String realmPath, final Long groupKey) {
+        return realmPath + "@" + groupKey;
     }
 
     public static boolean normalizingAddTo(final Set<String> realms, final String newRealm) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/core/misc/src/main/java/org/apache/syncope/core/misc/security/UnauthorizedException.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/security/UnauthorizedException.java b/core/misc/src/main/java/org/apache/syncope/core/misc/security/UnauthorizedException.java
index e5c2815..b2054cb 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/security/UnauthorizedException.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/security/UnauthorizedException.java
@@ -25,6 +25,9 @@ public class UnauthorizedException extends RuntimeException {
     private static final long serialVersionUID = 7540587364235915081L;
 
     public UnauthorizedException(final AnyTypeKind type, final Long key) {
-        super("Missing entitlement or realm administration for " + type + " " + key);
+        super("Missing entitlement or realm administration for "
+                + (key == null
+                        ? "new " + type
+                        : type + " " + key));
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
index 6fb5ec3..168241f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
@@ -30,6 +30,7 @@ import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.JPARole;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
@@ -95,6 +96,14 @@ public class JPARoleDAO extends AbstractDAO<Role, Long> implements RoleDAO {
 
     @Override
     public void delete(final Role role) {
+        TypedQuery<User> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAUser.class.getSimpleName() + " e WHERE :role MEMBER OF e.roles", User.class);
+        query.setParameter("role", role);
+
+        for (User user : query.getResultList()) {
+            user.remove(role);
+        }
+
         entityManager().remove(role);
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RoleTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RoleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RoleTest.java
index c9c7ef2..9764e07 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RoleTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RoleTest.java
@@ -160,4 +160,37 @@ public class RoleTest extends AbstractTest {
         dynRoleMemberships = findDynRoleMemberships(user);
         assertTrue(dynRoleMemberships.isEmpty());
     }
+
+    @Test
+    public void delete() {
+        // 0. create role
+        Role role = entityFactory.newEntity(Role.class);
+        role.setName("new");
+        role.addRealm(realmDAO.getRoot());
+        role.addRealm(realmDAO.find("/even/two"));
+        role.getEntitlements().add(Entitlement.LOG_LIST);
+        role.getEntitlements().add(Entitlement.LOG_SET_LEVEL);
+
+        role = roleDAO.save(role);
+        assertNotNull(role);
+
+        // 1. create user and assign that role
+        User user = entityFactory.newEntity(User.class);
+        user.setUsername("username");
+        user.setRealm(realmDAO.find("/even/two"));
+        user.add(role);
+
+        user = userDAO.save(user);
+        assertNotNull(user);
+
+        // 2. remove role
+        roleDAO.delete(role);
+
+        userDAO.flush();
+
+        // 3. verify that role was removed from user
+        user = userDAO.find(user.getKey());
+        assertNotNull(user);
+        assertTrue(user.getRoles().isEmpty());
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserSelfServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserSelfServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserSelfServiceImpl.java
index 18013d2..4930bbb 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserSelfServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserSelfServiceImpl.java
@@ -48,7 +48,7 @@ public class UserSelfServiceImpl extends AbstractServiceImpl implements UserSelf
             throw sce;
         }
 
-        UserTO created = logic.createSelf(userTO, storePassword);
+        UserTO created = logic.selfCreate(userTO, storePassword);
         return createResponse(created.getKey(), created);
     }
 
@@ -64,13 +64,13 @@ public class UserSelfServiceImpl extends AbstractServiceImpl implements UserSelf
 
     @Override
     public Response update(final UserMod userMod) {
-        UserTO updated = logic.updateSelf(userMod);
+        UserTO updated = logic.selfUpdate(userMod);
         return modificationResponse(updated);
     }
 
     @Override
     public Response delete() {
-        UserTO deleted = logic.deleteSelf();
+        UserTO deleted = logic.selfDelete();
         return modificationResponse(deleted);
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/c853f742/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AuthenticationITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AuthenticationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AuthenticationITCase.java
index 6378631..cdfd1b1 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AuthenticationITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AuthenticationITCase.java
@@ -28,7 +28,7 @@ import java.security.AccessControlException;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-
+import javax.ws.rs.core.Response;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
@@ -54,6 +54,7 @@ import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.syncope.common.lib.wrap.ResourceKey;
 import org.apache.syncope.common.rest.api.CollectionWrapper;
+import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.service.SchemaService;
 import org.apache.syncope.common.rest.api.service.UserService;
 import org.apache.syncope.core.misc.security.Encryptor;
@@ -234,6 +235,94 @@ public class AuthenticationITCase extends AbstractITCase {
     }
 
     @Test
+    public void delegatedUserCRUD() {
+        Long roleKey = null;
+        Long delegatedAdminKey = null;
+        try {
+            // 1. create role for full user administration, under realm /even/two
+            RoleTO role = new RoleTO();
+            role.setName("Delegated user admin");
+            role.getEntitlements().add(Entitlement.USER_CREATE);
+            role.getEntitlements().add(Entitlement.USER_UPDATE);
+            role.getEntitlements().add(Entitlement.USER_DELETE);
+            role.getEntitlements().add(Entitlement.USER_LIST);
+            role.getEntitlements().add(Entitlement.USER_READ);
+            role.getRealms().add("/even/two");
+
+            roleKey = Long.valueOf(roleService.create(role).getHeaderString(RESTHeaders.RESOURCE_KEY));
+            assertNotNull(roleKey);
+
+            // 2. as admin, create delegated admin user, and assign the role just created
+            UserTO delegatedAdmin = UserITCase.getUniqueSampleTO("admin@syncope.apache.org");
+            delegatedAdmin.getRoles().add(roleKey);
+            delegatedAdmin = createUser(delegatedAdmin);
+            delegatedAdminKey = delegatedAdmin.getKey();
+
+            // 3. instantiate a delegate user service client, for further operatins
+            UserService delegatedUserService =
+                    clientFactory.create(delegatedAdmin.getUsername(), "password123").getService(UserService.class);
+
+            // 4. as delegated, create user under realm / -> fail
+            UserTO user = UserITCase.getUniqueSampleTO("delegated@syncope.apache.org");
+            try {
+                delegatedUserService.create(user);
+                fail();
+            } catch (SyncopeClientException e) {
+                assertEquals(ClientExceptionType.Unauthorized, e.getType());
+            }
+
+            // 5. set realm to /even/two -> succeed
+            user.setRealm("/even/two");
+
+            Response response = delegatedUserService.create(user);
+            assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+
+            user = response.readEntity(UserTO.class);
+            assertEquals("surname", user.getPlainAttrMap().get("surname").getValues().get(0));
+
+            // 5. as delegated, update user attempting to move under realm / -> fail
+            UserMod userMod = new UserMod();
+            userMod.setKey(user.getKey());
+            userMod.setRealm("/odd");
+            userMod.getPlainAttrsToRemove().add("surname");
+            userMod.getPlainAttrsToUpdate().add(attrMod("surname", "surname2"));
+
+            try {
+                delegatedUserService.update(userMod);
+                fail();
+            } catch (SyncopeClientException e) {
+                assertEquals(ClientExceptionType.Unauthorized, e.getType());
+            }
+
+            // 6. revert realm change -> succeed
+            userMod.setRealm(null);
+
+            response = delegatedUserService.update(userMod);
+            assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+            user = response.readEntity(UserTO.class);
+            assertEquals("surname2", user.getPlainAttrMap().get("surname").getValues().get(0));
+
+            // 7. as delegated, delete user
+            delegatedUserService.delete(user.getKey());
+
+            try {
+                userService.read(user.getKey());
+                fail();
+            } catch (SyncopeClientException e) {
+                assertEquals(ClientExceptionType.NotFound, e.getType());
+            }
+        } finally {
+            if (roleKey != null) {
+                roleService.delete(roleKey);
+            }
+            if (delegatedAdminKey != null) {
+                userService.delete(delegatedAdminKey);
+            }
+        }
+    }
+
+    @Test
     public void checkFailedLogins() {
         UserTO userTO = UserITCase.getUniqueSampleTO("checkFailedLogin@syncope.apache.org");
         userTO.getRoles().add(2L);