[SYNCOPE-1067] Check that any USER / GROUP / ANYOBJECT UPDATE under DynRealm 
authorization cannot alter the set of DynRealms


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

Branch: refs/heads/master
Commit: f74ce5dee37f7e5157a3c84f00119d2ffa422e6f
Parents: 8683655
Author: Francesco Chicchiriccò <ilgro...@apache.org>
Authored: Fri Jun 9 11:42:25 2017 +0200
Committer: Francesco Chicchiriccò <ilgro...@apache.org>
Committed: Fri Jun 9 11:42:36 2017 +0200

----------------------------------------------------------------------
 .../syncope/core/logic/AbstractAnyLogic.java    |  70 +++++++++--
 .../syncope/core/logic/AnyObjectLogic.java      |  15 ++-
 .../apache/syncope/core/logic/GroupLogic.java   |  42 ++++---
 .../apache/syncope/core/logic/UserLogic.java    |  24 +++-
 .../api/search/SearchCondConverterTest.java     | 126 +++++++++----------
 .../persistence/jpa/dao/JPAAnyObjectDAO.java    |   3 +-
 .../core/persistence/jpa/dao/JPAGroupDAO.java   |   3 +-
 .../core/persistence/jpa/dao/JPAUserDAO.java    |   3 +-
 .../rest/cxf/RestServiceExceptionMapper.java    |   4 +-
 .../DelegatedAdministrationException.java       |   8 +-
 .../apache/syncope/fit/core/DynRealmITCase.java |  13 ++
 11 files changed, 207 insertions(+), 104 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/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 066b3d2..7c96979 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
@@ -167,7 +167,7 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P 
extends AnyPatch> ext
         return ImmutablePair.of(any, actions);
     }
 
-    protected ProvisioningResult<TO> after(
+    protected ProvisioningResult<TO> afterCreate(
             final TO input, final List<PropagationStatus> statuses, final 
List<LogicActions> actions) {
 
         TO any = input;
@@ -183,6 +183,53 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P 
extends AnyPatch> ext
         return result;
     }
 
+    protected ProvisioningResult<TO> afterUpdate(
+            final TO input,
+            final List<PropagationStatus> statuses,
+            final List<LogicActions> actions,
+            final boolean authDynRealms,
+            final Set<String> dynRealmsBefore) {
+
+        Set<String> dynRealmsAfter = new HashSet<>(input.getDynRealms());
+        if (authDynRealms && !dynRealmsBefore.equals(dynRealmsAfter)) {
+            throw new DelegatedAdministrationException(
+                    this instanceof UserLogic
+                            ? AnyTypeKind.USER
+                            : this instanceof GroupLogic
+                                    ? AnyTypeKind.GROUP
+                                    : AnyTypeKind.ANY_OBJECT,
+                    input.getKey());
+        }
+
+        TO any = input;
+
+        for (LogicActions action : actions) {
+            any = action.afterUpdate(any);
+        }
+
+        ProvisioningResult<TO> result = new ProvisioningResult<>();
+        result.setEntity(any);
+        result.getPropagationStatuses().addAll(statuses);
+
+        return result;
+    }
+
+    protected ProvisioningResult<TO> afterDelete(
+            final TO input, final List<PropagationStatus> statuses, final 
List<LogicActions> actions) {
+
+        TO any = input;
+
+        for (LogicActions action : actions) {
+            any = action.afterDelete(any);
+        }
+
+        ProvisioningResult<TO> result = new ProvisioningResult<>();
+        result.setEntity(any);
+        result.getPropagationStatuses().addAll(statuses);
+
+        return result;
+    }
+
     private static class StartsWithPredicate implements Predicate<String> {
 
         private final Collection<String> targets;
@@ -204,6 +251,14 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P 
extends AnyPatch> ext
 
     }
 
+    protected static class DynRealmsPredicate implements Predicate<String> {
+
+        @Override
+        public boolean evaluate(final String realm) {
+            return !realm.startsWith("/");
+        }
+    }
+
     protected Set<String> getEffectiveRealms(final Set<String> allowedRealms, 
final String requestedRealm) {
         Set<String> allowed = RealmUtils.normalize(allowedRealms);
         Set<String> requested = new HashSet<>();
@@ -214,18 +269,12 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, 
P extends AnyPatch> ext
         CollectionUtils.select(allowed, new StartsWithPredicate(requested), 
effective);
 
         // includes dynamic realms
-        CollectionUtils.select(allowedRealms, new Predicate<String>() {
-
-            @Override
-            public boolean evaluate(final String realm) {
-                return !realm.startsWith("/");
-            }
-        }, effective);
+        CollectionUtils.select(allowedRealms, new DynRealmsPredicate(), 
effective);
 
         return effective;
     }
 
-    protected void securityChecks(final Set<String> effectiveRealms, final 
String realm, final String key) {
+    protected boolean securityChecks(final Set<String> effectiveRealms, final 
String realm, final String key) {
         boolean authorized = IterableUtils.matchesAny(effectiveRealms, new 
Predicate<String>() {
 
             @Override
@@ -243,6 +292,7 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P 
extends AnyPatch> ext
         }
         if (!authorized) {
             throw new DelegatedAdministrationException(
+                    realm,
                     this instanceof UserLogic
                             ? AnyTypeKind.USER
                             : this instanceof GroupLogic
@@ -250,6 +300,8 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P 
extends AnyPatch> ext
                                     : AnyTypeKind.ANY_OBJECT,
                     key);
         }
+
+        return IterableUtils.matchesAny(effectiveRealms, new 
DynRealmsPredicate());
     }
 
     public abstract Date findLastChange(String key);

http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/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 c585dd0..8805221 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.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.apache.commons.collections4.CollectionUtils;
@@ -158,7 +159,7 @@ public class AnyObjectLogic extends 
AbstractAnyLogic<AnyObjectTO, AnyObjectPatch
 
         Pair<String, List<PropagationStatus>> created = 
provisioningManager.create(before.getLeft(), nullPriorityAsync);
 
-        return after(binder.getAnyObjectTO(created.getKey()), 
created.getRight(), before.getRight());
+        return afterCreate(binder.getAnyObjectTO(created.getKey()), 
created.getRight(), before.getRight());
     }
 
     @Override
@@ -166,6 +167,7 @@ public class AnyObjectLogic extends 
AbstractAnyLogic<AnyObjectTO, AnyObjectPatch
             final AnyObjectPatch anyObjectPatch, final boolean 
nullPriorityAsync) {
 
         AnyObjectTO anyObjectTO = 
binder.getAnyObjectTO(anyObjectPatch.getKey());
+        Set<String> dynRealmsBefore = new 
HashSet<>(anyObjectTO.getDynRealms());
         Pair<AnyObjectPatch, List<LogicActions>> before = 
beforeUpdate(anyObjectPatch, anyObjectTO.getRealm());
 
         String realm =
@@ -175,11 +177,16 @@ public class AnyObjectLogic extends 
AbstractAnyLogic<AnyObjectTO, AnyObjectPatch
         Set<String> effectiveRealms = getEffectiveRealms(
                 
AuthContextUtils.getAuthorizations().get(AnyEntitlement.UPDATE.getFor(anyObjectTO.getType())),
                 realm);
-        securityChecks(effectiveRealms, realm, before.getLeft().getKey());
+        boolean authDynRealms = securityChecks(effectiveRealms, realm, 
before.getLeft().getKey());
 
         Pair<String, List<PropagationStatus>> updated = 
provisioningManager.update(anyObjectPatch, nullPriorityAsync);
 
-        return after(binder.getAnyObjectTO(updated.getKey()), 
updated.getRight(), before.getRight());
+        return afterUpdate(
+                binder.getAnyObjectTO(updated.getKey()),
+                updated.getRight(),
+                before.getRight(),
+                authDynRealms,
+                dynRealmsBefore);
     }
 
     @Override
@@ -197,7 +204,7 @@ public class AnyObjectLogic extends 
AbstractAnyLogic<AnyObjectTO, AnyObjectPatch
         AnyObjectTO anyObjectTO = new AnyObjectTO();
         anyObjectTO.setKey(before.getLeft().getKey());
 
-        return after(anyObjectTO, statuses, before.getRight());
+        return afterDelete(anyObjectTO, statuses, before.getRight());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/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 1ddc108..ca3e080 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,6 +22,7 @@ import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -109,17 +110,22 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, 
GroupPatch> {
     protected EntityFactory entityFactory;
 
     @Override
-    protected void securityChecks(final Set<String> effectiveRealms, final 
String realm, final String key) {
-        if (!IterableUtils.matchesAny(effectiveRealms, new Predicate<String>() 
{
+    protected boolean securityChecks(final Set<String> effectiveRealms, final 
String realm, final String key) {
+        boolean authorized = IterableUtils.matchesAny(effectiveRealms, new 
Predicate<String>() {
 
             @Override
             public boolean evaluate(final String ownedRealm) {
                 return realm.startsWith(ownedRealm) || 
ownedRealm.equals(RealmUtils.getGroupOwnerRealm(realm, key));
             }
-        })) {
-
-            throw new DelegatedAdministrationException(AnyTypeKind.GROUP, key);
+        });
+        if (!authorized) {
+            authorized = 
!CollectionUtils.intersection(groupDAO.findDynRealms(key), 
effectiveRealms).isEmpty();
+        }
+        if (!authorized) {
+            throw new DelegatedAdministrationException(realm, 
AnyTypeKind.GROUP, key);
         }
+
+        return IterableUtils.matchesAny(effectiveRealms, new 
AbstractAnyLogic.DynRealmsPredicate());
     }
 
     @Transactional(readOnly = true)
@@ -239,25 +245,33 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, 
GroupPatch> {
         Pair<String, List<PropagationStatus>> created =
                 provisioningManager.create(before.getLeft(), 
nullPriorityAsync);
 
-        return after(binder.getGroupTO(created.getKey()), created.getRight(), 
before.getRight());
+        return afterCreate(binder.getGroupTO(created.getKey()), 
created.getRight(), before.getRight());
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.GROUP_UPDATE + "')")
     @Override
     public ProvisioningResult<GroupTO> update(final GroupPatch groupPatch, 
final boolean nullPriorityAsync) {
         GroupTO groupTO = binder.getGroupTO(groupPatch.getKey());
+        Set<String> dynRealmsBefore = new HashSet<>(groupTO.getDynRealms());
         Pair<GroupPatch, List<LogicActions>> before = beforeUpdate(groupPatch, 
groupTO.getRealm());
 
-        if (before.getLeft().getRealm() != null && 
StringUtils.isNotBlank(before.getLeft().getRealm().getValue())) {
-            Set<String> effectiveRealms = getEffectiveRealms(
-                    
AuthContextUtils.getAuthorizations().get(StandardEntitlement.USER_UPDATE),
-                    before.getLeft().getRealm().getValue());
-            securityChecks(effectiveRealms, 
before.getLeft().getRealm().getValue(), before.getLeft().getKey());
-        }
+        String realm =
+                before.getLeft().getRealm() != null && 
StringUtils.isNotBlank(before.getLeft().getRealm().getValue())
+                ? before.getLeft().getRealm().getValue()
+                : groupTO.getRealm();
+        Set<String> effectiveRealms = getEffectiveRealms(
+                
AuthContextUtils.getAuthorizations().get(StandardEntitlement.GROUP_UPDATE),
+                realm);
+        boolean authDynRealms = securityChecks(effectiveRealms, realm, 
before.getLeft().getKey());
 
         Pair<String, List<PropagationStatus>> updated = 
provisioningManager.update(groupPatch, nullPriorityAsync);
 
-        return after(binder.getGroupTO(updated.getKey()), updated.getRight(), 
before.getRight());
+        return afterUpdate(
+                binder.getGroupTO(updated.getKey()),
+                updated.getRight(),
+                before.getRight(),
+                authDynRealms,
+                dynRealmsBefore);
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.GROUP_DELETE + "')")
@@ -290,7 +304,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, 
GroupPatch> {
         GroupTO groupTO = new GroupTO();
         groupTO.setKey(before.getLeft().getKey());
 
-        return after(groupTO, statuses, before.getRight());
+        return afterDelete(groupTO, statuses, before.getRight());
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.GROUP_UPDATE + "')")

http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/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 02d88cb..54a43f3 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,6 +23,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.apache.commons.collections4.CollectionUtils;
@@ -203,7 +204,8 @@ public class UserLogic extends AbstractAnyLogic<UserTO, 
UserPatch> {
         Pair<String, List<PropagationStatus>> created =
                 provisioningManager.create(before.getLeft(), storePassword, 
nullPriorityAsync);
 
-        return after(binder.returnUserTO(binder.getUserTO(created.getKey())), 
created.getRight(), before.getRight());
+        return afterCreate(
+                binder.returnUserTO(binder.getUserTO(created.getKey())), 
created.getRight(), before.getRight());
     }
 
     @PreAuthorize("isAuthenticated() and not(hasRole('" + 
StandardEntitlement.ANONYMOUS + "'))")
@@ -223,8 +225,10 @@ public class UserLogic extends AbstractAnyLogic<UserTO, 
UserPatch> {
             final UserPatch userPatch, final boolean self, final boolean 
nullPriorityAsync) {
 
         UserTO userTO = binder.getUserTO(userPatch.getKey());
+        Set<String> dynRealmsBefore = new HashSet<>(userTO.getDynRealms());
         Pair<UserPatch, List<LogicActions>> before = beforeUpdate(userPatch, 
userTO.getRealm());
 
+        boolean authDynRealms = false;
         if (!self
                 && before.getLeft().getRealm() != null
                 && 
StringUtils.isNotBlank(before.getLeft().getRealm().getValue())) {
@@ -232,12 +236,18 @@ public class UserLogic extends AbstractAnyLogic<UserTO, 
UserPatch> {
             Set<String> effectiveRealms = getEffectiveRealms(
                     
AuthContextUtils.getAuthorizations().get(StandardEntitlement.USER_UPDATE),
                     before.getLeft().getRealm().getValue());
-            securityChecks(effectiveRealms, 
before.getLeft().getRealm().getValue(), before.getLeft().getKey());
+            authDynRealms =
+                    securityChecks(effectiveRealms, 
before.getLeft().getRealm().getValue(), before.getLeft().getKey());
         }
 
         Pair<String, List<PropagationStatus>> updated = 
provisioningManager.update(before.getLeft(), nullPriorityAsync);
 
-        return after(binder.returnUserTO(binder.getUserTO(updated.getKey())), 
updated.getRight(), before.getRight());
+        return afterUpdate(
+                binder.returnUserTO(binder.getUserTO(updated.getKey())),
+                updated.getRight(),
+                before.getRight(),
+                authDynRealms,
+                dynRealmsBefore);
     }
 
     protected Pair<String, List<PropagationStatus>> setStatusOnWfAdapter(
@@ -278,10 +288,12 @@ public class UserLogic extends AbstractAnyLogic<UserTO, 
UserPatch> {
         statusPatch.setKey(toUpdate.getKey());
         Pair<String, List<PropagationStatus>> updated = 
setStatusOnWfAdapter(statusPatch, nullPriorityAsync);
 
-        return after(
+        return afterUpdate(
                 binder.returnUserTO(binder.getUserTO(updated.getKey())),
                 updated.getRight(),
-                Collections.<LogicActions>emptyList());
+                Collections.<LogicActions>emptyList(),
+                false,
+                Collections.<String>emptySet());
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.MUST_CHANGE_PASSWORD + 
"')")
@@ -371,7 +383,7 @@ public class UserLogic extends AbstractAnyLogic<UserTO, 
UserPatch> {
             deletedTO = binder.getUserTO(before.getLeft().getKey());
         }
 
-        return after(binder.returnUserTO(deletedTO), statuses, 
before.getRight());
+        return afterDelete(binder.returnUserTO(deletedTO), statuses, 
before.getRight());
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.USER_UPDATE + "')")

http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
 
b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
index 08b2f12..01fb922 100644
--- 
a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
+++ 
b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
@@ -43,231 +43,227 @@ public class SearchCondConverterTest {
 
     @Test
     public void eq() {
-        String fiqlExpression = new 
UserFiqlSearchConditionBuilder().is("username").equalTo("rossini").query();
-        assertEquals("username==rossini", fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().is("username").equalTo("rossini").query();
+        assertEquals("username==rossini", fiql);
 
         AnyCond attrCond = new AnyCond(AttributeCond.Type.EQ);
         attrCond.setSchema("username");
         attrCond.setExpression("rossini");
         SearchCond simpleCond = SearchCond.getLeafCond(attrCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void ieq() {
-        String fiqlExpression = new UserFiqlSearchConditionBuilder().
-                is("username").equalToIgnoreCase("rossini").query();
-        assertEquals("username=~rossini", fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("rossini").query();
+        assertEquals("username=~rossini", fiql);
 
         AnyCond attrCond = new AnyCond(AttributeCond.Type.IEQ);
         attrCond.setSchema("username");
         attrCond.setExpression("rossini");
         SearchCond simpleCond = SearchCond.getLeafCond(attrCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void nieq() {
-        String fiqlExpression = new UserFiqlSearchConditionBuilder().
-                is("username").notEqualTolIgnoreCase("rossini").query();
-        assertEquals("username!~rossini", fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("rossini").query();
+        assertEquals("username!~rossini", fiql);
 
         AnyCond attrCond = new AnyCond(AttributeCond.Type.IEQ);
         attrCond.setSchema("username");
         attrCond.setExpression("rossini");
         SearchCond simpleCond = SearchCond.getNotLeafCond(attrCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void like() {
-        String fiqlExpression = new 
UserFiqlSearchConditionBuilder().is("username").equalTo("ros*").query();
-        assertEquals("username==ros*", fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().is("username").equalTo("ros*").query();
+        assertEquals("username==ros*", fiql);
 
         AttributeCond attrCond = new AnyCond(AttributeCond.Type.LIKE);
         attrCond.setSchema("username");
         attrCond.setExpression("ros%");
         SearchCond simpleCond = SearchCond.getLeafCond(attrCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void ilike() {
-        String fiqlExpression = new 
UserFiqlSearchConditionBuilder().is("username").
-                equalToIgnoreCase("ros*").query();
-        assertEquals("username=~ros*", fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("ros*").query();
+        assertEquals("username=~ros*", fiql);
 
         AttributeCond attrCond = new AnyCond(AttributeCond.Type.ILIKE);
         attrCond.setSchema("username");
         attrCond.setExpression("ros%");
         SearchCond simpleCond = SearchCond.getLeafCond(attrCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void nilike() {
-        String fiqlExpression = new 
UserFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("ros*").
-                query();
-        assertEquals("username!~ros*", fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("ros*").query();
+        assertEquals("username!~ros*", fiql);
 
         AttributeCond attrCond = new AnyCond(AttributeCond.Type.ILIKE);
         attrCond.setSchema("username");
         attrCond.setExpression("ros%");
         SearchCond simpleCond = SearchCond.getNotLeafCond(attrCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void isNull() {
-        String fiqlExpression = new 
UserFiqlSearchConditionBuilder().is("loginDate").nullValue().query();
-        assertEquals("loginDate==" + SpecialAttr.NULL, fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().is("loginDate").nullValue().query();
+        assertEquals("loginDate==" + SpecialAttr.NULL, fiql);
 
         AttributeCond attrCond = new AttributeCond(AttributeCond.Type.ISNULL);
         attrCond.setSchema("loginDate");
         SearchCond simpleCond = SearchCond.getLeafCond(attrCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void isNotNull() {
-        String fiqlExpression = new 
UserFiqlSearchConditionBuilder().is("loginDate").notNullValue().query();
-        assertEquals("loginDate!=" + SpecialAttr.NULL, fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().is("loginDate").notNullValue().query();
+        assertEquals("loginDate!=" + SpecialAttr.NULL, fiql);
 
         AttributeCond attrCond = new 
AttributeCond(AttributeCond.Type.ISNOTNULL);
         attrCond.setSchema("loginDate");
         SearchCond simpleCond = SearchCond.getLeafCond(attrCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void relationships() {
-        String fiqlExpression = new UserFiqlSearchConditionBuilder().
+        String fiql = new UserFiqlSearchConditionBuilder().
                 
inRelationships("ca20ffca-1305-442f-be9a-3723a0cd88ca").query();
-        assertEquals(SpecialAttr.RELATIONSHIPS + 
"==ca20ffca-1305-442f-be9a-3723a0cd88ca", fiqlExpression);
+        assertEquals(SpecialAttr.RELATIONSHIPS + 
"==ca20ffca-1305-442f-be9a-3723a0cd88ca", fiql);
 
         RelationshipCond relationshipCond = new RelationshipCond();
         relationshipCond.setAnyObject("ca20ffca-1305-442f-be9a-3723a0cd88ca");
         SearchCond simpleCond = SearchCond.getLeafCond(relationshipCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void relationshipTypes() {
-        String fiqlExpression = new 
UserFiqlSearchConditionBuilder().inRelationshipTypes("type1").query();
-        assertEquals(SpecialAttr.RELATIONSHIP_TYPES + "==type1", 
fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().inRelationshipTypes("type1").query();
+        assertEquals(SpecialAttr.RELATIONSHIP_TYPES + "==type1", fiql);
 
         RelationshipTypeCond relationshipCond = new RelationshipTypeCond();
         relationshipCond.setRelationshipTypeKey("type1");
         SearchCond simpleCond = SearchCond.getLeafCond(relationshipCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
 
-        fiqlExpression = new 
AnyObjectFiqlSearchConditionBuilder("PRINTER").inRelationshipTypes("neighborhood").query();
+        fiql = new 
AnyObjectFiqlSearchConditionBuilder("PRINTER").inRelationshipTypes("neighborhood").query();
         assertEquals(
                 SpecialAttr.RELATIONSHIP_TYPES + "==neighborhood;" + 
SpecialAttr.TYPE + "==PRINTER",
-                fiqlExpression);
+                fiql);
     }
 
     @Test
     public void groups() {
-        String fiqlExpression = new UserFiqlSearchConditionBuilder().
+        String fiql = new UserFiqlSearchConditionBuilder().
                 inGroups("e7ff94e8-19c9-4f0a-b8b7-28327edbf6ed").query();
-        assertEquals(SpecialAttr.GROUPS + 
"==e7ff94e8-19c9-4f0a-b8b7-28327edbf6ed", fiqlExpression);
+        assertEquals(SpecialAttr.GROUPS + 
"==e7ff94e8-19c9-4f0a-b8b7-28327edbf6ed", fiql);
 
         MembershipCond groupCond = new MembershipCond();
         groupCond.setGroup("e7ff94e8-19c9-4f0a-b8b7-28327edbf6ed");
         SearchCond simpleCond = SearchCond.getLeafCond(groupCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void roles() {
-        String fiqlExpression = new 
UserFiqlSearchConditionBuilder().inRoles("User reviewer").query();
-        assertEquals(SpecialAttr.ROLES + "==User reviewer", fiqlExpression);
+        String fiql = new UserFiqlSearchConditionBuilder().inRoles("User 
reviewer").query();
+        assertEquals(SpecialAttr.ROLES + "==User reviewer", fiql);
 
         RoleCond roleCond = new RoleCond();
         roleCond.setRole("User reviewer");
         SearchCond simpleCond = SearchCond.getLeafCond(roleCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void dynRealms() {
         String dynRealm = UUID.randomUUID().toString();
-        String fiqlExpression = new 
UserFiqlSearchConditionBuilder().inDynRealms(dynRealm).query();
-        assertEquals(SpecialAttr.DYNREALMS + "==" + dynRealm, fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().inDynRealms(dynRealm).query();
+        assertEquals(SpecialAttr.DYNREALMS + "==" + dynRealm, fiql);
 
         DynRealmCond dynRealmCond = new DynRealmCond();
         dynRealmCond.setDynRealm(dynRealm);
         SearchCond simpleCond = SearchCond.getLeafCond(dynRealmCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void resources() {
-        String fiqlExpression = new 
UserFiqlSearchConditionBuilder().hasResources("resource-ldap").query();
-        assertEquals(SpecialAttr.RESOURCES + "==resource-ldap", 
fiqlExpression);
+        String fiql = new 
UserFiqlSearchConditionBuilder().hasResources("resource-ldap").query();
+        assertEquals(SpecialAttr.RESOURCES + "==resource-ldap", fiql);
 
         ResourceCond resCond = new ResourceCond();
         resCond.setResourceKey("resource-ldap");
         SearchCond simpleCond = SearchCond.getLeafCond(resCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void assignable() {
-        String fiqlExpression = new 
GroupFiqlSearchConditionBuilder().isAssignable().query();
-        assertEquals(SpecialAttr.ASSIGNABLE + "==" + SpecialAttr.NULL, 
fiqlExpression);
+        String fiql = new 
GroupFiqlSearchConditionBuilder().isAssignable().query();
+        assertEquals(SpecialAttr.ASSIGNABLE + "==" + SpecialAttr.NULL, fiql);
 
         AssignableCond assignableCond = new AssignableCond();
         assignableCond.setRealmFullPath("/even/two");
         SearchCond simpleCond = SearchCond.getLeafCond(assignableCond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression, 
"/even/two"));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql, 
"/even/two"));
     }
 
     @Test
     public void type() {
-        String fiqlExpression = new 
AnyObjectFiqlSearchConditionBuilder("PRINTER").query();
-        assertEquals(SpecialAttr.TYPE + "==PRINTER", fiqlExpression);
+        String fiql = new 
AnyObjectFiqlSearchConditionBuilder("PRINTER").query();
+        assertEquals(SpecialAttr.TYPE + "==PRINTER", fiql);
 
         AnyTypeCond acond = new AnyTypeCond();
         acond.setAnyTypeKey("PRINTER");
         SearchCond simpleCond = SearchCond.getLeafCond(acond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void member() {
-        String fiqlExpression = new 
GroupFiqlSearchConditionBuilder().withMembers("rossini").query();
-        assertEquals(SpecialAttr.MEMBER + "==rossini", fiqlExpression);
+        String fiql = new 
GroupFiqlSearchConditionBuilder().withMembers("rossini").query();
+        assertEquals(SpecialAttr.MEMBER + "==rossini", fiql);
 
         MemberCond mcond = new MemberCond();
         mcond.setMember("rossini");
         SearchCond simpleCond = SearchCond.getLeafCond(mcond);
 
-        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(simpleCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void and() {
-        String fiqlExpression = new UserFiqlSearchConditionBuilder().
+        String fiql = new UserFiqlSearchConditionBuilder().
                 
is("fullname").equalTo("*o*").and("fullname").equalTo("*i*").query();
-        assertEquals("fullname==*o*;fullname==*i*", fiqlExpression);
+        assertEquals("fullname==*o*;fullname==*i*", fiql);
 
         AttributeCond fullnameLeafCond1 = new 
AttributeCond(AttributeCond.Type.LIKE);
         fullnameLeafCond1.setSchema("fullname");
@@ -279,17 +275,17 @@ public class SearchCondConverterTest {
                 SearchCond.getLeafCond(fullnameLeafCond1),
                 SearchCond.getLeafCond(fullnameLeafCond2));
 
-        assertEquals(andCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(andCond, SearchCondConverter.convert(fiql));
     }
 
     @Test
     public void or() {
-        String fiqlExpression = new UserFiqlSearchConditionBuilder().
+        String fiql = new UserFiqlSearchConditionBuilder().
                 is("fullname").equalTo("*o*", "*i*", "*ini").query();
-        assertEquals("fullname==*o*,fullname==*i*,fullname==*ini", 
fiqlExpression);
-        fiqlExpression = new UserFiqlSearchConditionBuilder().
+        assertEquals("fullname==*o*,fullname==*i*,fullname==*ini", fiql);
+        fiql = new UserFiqlSearchConditionBuilder().
                 
is("fullname").equalTo("*o*").or("fullname").equalTo("*i*").or("fullname").equalTo("*ini").query();
-        assertEquals("fullname==*o*,fullname==*i*,fullname==*ini", 
fiqlExpression);
+        assertEquals("fullname==*o*,fullname==*i*,fullname==*ini", fiql);
 
         AttributeCond fullnameLeafCond1 = new 
AttributeCond(AttributeCond.Type.LIKE);
         fullnameLeafCond1.setSchema("fullname");
@@ -306,7 +302,7 @@ public class SearchCondConverterTest {
                         SearchCond.getLeafCond(fullnameLeafCond2),
                         SearchCond.getLeafCond(fullnameLeafCond3)));
 
-        assertEquals(orCond, SearchCondConverter.convert(fiqlExpression));
+        assertEquals(orCond, SearchCondConverter.convert(fiql));
     }
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
index e0545d2..3cf4376 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
@@ -147,7 +147,8 @@ public class JPAAnyObjectDAO extends 
AbstractAnyDAO<AnyObject> implements AnyObj
             authorized = 
!CollectionUtils.intersection(findDynRealms(anyObject.getKey()), 
authRealms).isEmpty();
         }
         if (authRealms == null || authRealms.isEmpty() || !authorized) {
-            throw new DelegatedAdministrationException(AnyTypeKind.ANY_OBJECT, 
anyObject.getKey());
+            throw new DelegatedAdministrationException(
+                    anyObject.getRealm().getFullPath(), 
AnyTypeKind.ANY_OBJECT, anyObject.getKey());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
index 6cec27a..4c79d1b 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
@@ -171,7 +171,8 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> 
implements GroupDAO {
         }
 
         if (authRealms == null || authRealms.isEmpty() || !authorized) {
-            throw new DelegatedAdministrationException(AnyTypeKind.GROUP, 
group.getKey());
+            throw new DelegatedAdministrationException(
+                    group.getRealm().getFullPath(), AnyTypeKind.GROUP, 
group.getKey());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index afa83f4..f3ada03 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -190,7 +190,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> 
implements UserDAO {
                 authorized = 
!CollectionUtils.intersection(findDynRealms(user.getKey()), 
authRealms).isEmpty();
             }
             if (authRealms == null || authRealms.isEmpty() || !authorized) {
-                throw new DelegatedAdministrationException(AnyTypeKind.USER, 
user.getKey());
+                throw new DelegatedAdministrationException(
+                        user.getRealm().getFullPath(), AnyTypeKind.USER, 
user.getKey());
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
----------------------------------------------------------------------
diff --git 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
index 1ff733b..718216e 100644
--- 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
+++ 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
@@ -99,7 +99,9 @@ public class RestServiceExceptionMapper implements 
ExceptionMapper<Exception> {
             builder = sce.isComposite()
                     ? 
getSyncopeClientCompositeExceptionResponse(sce.asComposite())
                     : getSyncopeClientExceptionResponse(sce);
-        } else if (ex instanceof DelegatedAdministrationException) {
+        } else if (ex instanceof DelegatedAdministrationException
+                || ExceptionUtils.getRootCause(ex) instanceof 
DelegatedAdministrationException) {
+
             builder = builder(ClientExceptionType.DelegatedAdministration, 
ExceptionUtils.getRootCauseMessage(ex));
         } else if (ex instanceof EntityExistsException || ex instanceof 
DuplicateException
                 || ex instanceof PersistenceException && ex.getCause() 
instanceof EntityExistsException) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/core/spring/src/main/java/org/apache/syncope/core/spring/security/DelegatedAdministrationException.java
----------------------------------------------------------------------
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DelegatedAdministrationException.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DelegatedAdministrationException.java
index be378a5..0ee414a 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DelegatedAdministrationException.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DelegatedAdministrationException.java
@@ -24,10 +24,14 @@ public class DelegatedAdministrationException extends 
RuntimeException {
 
     private static final long serialVersionUID = 7540587364235915081L;
 
-    public DelegatedAdministrationException(final AnyTypeKind type, final 
String key) {
-        super("Missing entitlement or realm administration for "
+    public DelegatedAdministrationException(final String realm, final 
AnyTypeKind type, final String key) {
+        super("Missing entitlement or realm administration under " + realm + " 
for "
                 + (key == null
                         ? "new " + type
                         : type + " " + key));
     }
+
+    public DelegatedAdministrationException(final AnyTypeKind type, final 
String key) {
+        super("The requested UPDATE would alter the set of dynamic realms for 
" + type + " " + key);
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f74ce5de/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
index 1ad59ea..caf1623 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
@@ -41,6 +41,7 @@ import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.RoleTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.common.rest.api.beans.AnyQuery;
 import org.apache.syncope.common.rest.api.service.DynRealmService;
@@ -182,6 +183,18 @@ public class DynRealmITCase extends AbstractITCase {
             // USER_UPDATE
             UserPatch userPatch = new UserPatch();
             userPatch.setKey(userKey);
+            userPatch.getResources().add(new StringPatchItem.Builder().
+                    
value(RESOURCE_NAME_LDAP).operation(PatchOperation.DELETE).build());
+            // this will fail because unassigning resource-ldap would result 
in removing the user
+            // from the dynamic realm
+            try {
+                delegatedUserService.update(userPatch);
+                fail();
+            } catch (SyncopeClientException e) {
+                assertEquals(ClientExceptionType.DelegatedAdministration, 
e.getType());
+            }
+            // this will succeed instead
+            userPatch.getResources().clear();
             userPatch.getResources().add(new 
StringPatchItem.Builder().value(RESOURCE_NAME_NOPROPAGATION).build());
             user = delegatedUserService.update(userPatch).
                     readEntity(new GenericType<ProvisioningResult<UserTO>>() {

Reply via email to