Repository: syncope Updated Branches: refs/heads/2_0_X d6d52e1ac -> 1ad305580 refs/heads/master 8683655c6 -> f74ce5dee
[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/1ad30558 Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/1ad30558 Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/1ad30558 Branch: refs/heads/2_0_X Commit: 1ad3055802d6195dbaaeee186e512d38defdcaad Parents: d6d52e1 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:25 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/1ad30558/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/1ad30558/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/1ad30558/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/1ad30558/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/1ad30558/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/1ad30558/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/1ad30558/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/1ad30558/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/1ad30558/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/1ad30558/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/1ad30558/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>>() {