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 2017/06/08 15:55:13 UTC

[1/4] syncope git commit: [SYNCOPE-1067] Core modifications

Repository: syncope
Updated Branches:
  refs/heads/2_0_X b61b092a8 -> 062c87a25
  refs/heads/master 6816f17ec -> a1bb67231


http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/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 1b7374a..6cec27a 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
@@ -28,6 +28,7 @@ import java.util.Set;
 import javax.persistence.NoResultException;
 import javax.persistence.Query;
 import javax.persistence.TypedQuery;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -165,6 +166,10 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
                         || realm.equals(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey()));
             }
         });
+        if (!authorized) {
+            authorized = !CollectionUtils.intersection(findDynRealms(group.getKey()), authRealms).isEmpty();
+        }
+
         if (authRealms == null || authRealms.isEmpty() || !authorized) {
             throw new DelegatedAdministrationException(AnyTypeKind.GROUP, group.getKey());
         }
@@ -315,11 +320,15 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             }
         }
 
+        dynRealmDAO().refreshDynMemberships(merged);
+
         return merged;
     }
 
     @Override
     public void delete(final Group group) {
+        dynRealmDAO().removeDynMemberships(group.getKey());
+
         for (AMembership membership : findAMemberships(group)) {
             AnyObject leftEnd = membership.getLeftEnd();
             leftEnd.getMemberships().remove(membership);
@@ -349,6 +358,9 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd, AuthContextUtils.getDomain()));
         }
 
+        clearUDynMembers(group);
+        clearADynMembers(group);
+
         entityManager().remove(group);
         publisher.publishEvent(
                 new AnyDeletedEvent(this, AnyTypeKind.GROUP, group.getKey(), AuthContextUtils.getDomain()));
@@ -427,7 +439,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
 
     @Override
     public void removeDynMemberships(final AnyObject anyObject) {
-        List<Group> dynGroups = anyObjectDAO().findDynGroups(anyObject);
+        List<Group> dynGroups = anyObjectDAO().findDynGroups(anyObject.getKey());
 
         Query delete = entityManager().createNativeQuery("DELETE FROM " + ADYNMEMB_TABLE + " WHERE any_id=?");
         delete.setParameter(1, anyObject.getKey());
@@ -501,7 +513,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
 
     @Override
     public void removeDynMemberships(final User user) {
-        List<Group> dynGroups = userDAO().findDynGroups(user);
+        List<Group> dynGroups = userDAO().findDynGroups(user.getKey());
 
         Query delete = entityManager().createNativeQuery("DELETE FROM " + UDYNMEMB_TABLE + " WHERE any_id=?");
         delete.setParameter(1, user.getKey());

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/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 9796f4d..367ee8d 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
@@ -121,6 +121,8 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
             publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user, AuthContextUtils.getDomain()));
         }
 
+        clearDynMembers(role);
+
         entityManager().remove(role);
     }
 
@@ -183,9 +185,9 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
     }
 
     @Override
-    public void removeDynMemberships(final User user) {
+    public void removeDynMemberships(final String key) {
         Query delete = entityManager().createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE any_id=?");
-        delete.setParameter(1, user.getKey());
+        delete.setParameter(1, key);
         delete.executeUpdate();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/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 40db0c4..afa83f4 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
@@ -186,6 +186,9 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                     return user.getRealm().getFullPath().startsWith(realm);
                 }
             });
+            if (!authorized) {
+                authorized = !CollectionUtils.intersection(findDynRealms(user.getKey()), authRealms).isEmpty();
+            }
             if (authRealms == null || authRealms.isEmpty() || !authorized) {
                 throw new DelegatedAdministrationException(AnyTypeKind.USER, user.getKey());
             }
@@ -448,14 +451,16 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
         roleDAO.refreshDynMemberships(merged);
         groupDAO().refreshDynMemberships(merged);
+        dynRealmDAO().refreshDynMemberships(merged);
 
         return merged;
     }
 
     @Override
     public void delete(final User user) {
-        roleDAO.removeDynMemberships(user);
-        groupDAO.removeDynMemberships(user);
+        roleDAO.removeDynMemberships(user.getKey());
+        groupDAO().removeDynMemberships(user);
+        dynRealmDAO().removeDynMemberships(user.getKey());
 
         AccessToken accessToken = accessTokenDAO.findByOwner(user.getUsername());
         if (accessToken != null) {
@@ -470,21 +475,21 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
     public Collection<Role> findAllRoles(final User user) {
-        return CollectionUtils.union(user.getRoles(), findDynRoles(user));
+        return CollectionUtils.union(user.getRoles(), findDynRoles(user.getKey()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
-    public List<Role> findDynRoles(final User user) {
+    public List<Role> findDynRoles(final String key) {
         Query query = entityManager().createNativeQuery(
                 "SELECT role_id FROM " + JPARoleDAO.DYNMEMB_TABLE + " WHERE any_id=?");
-        query.setParameter(1, user.getKey());
+        query.setParameter(1, key);
 
         List<Role> result = new ArrayList<>();
-        for (Object key : query.getResultList()) {
-            String actualKey = key instanceof Object[]
-                    ? (String) ((Object[]) key)[0]
-                    : ((String) key);
+        for (Object resultKey : query.getResultList()) {
+            String actualKey = resultKey instanceof Object[]
+                    ? (String) ((Object[]) resultKey)[0]
+                    : ((String) resultKey);
 
             Role role = roleDAO.find(actualKey);
             if (role == null) {
@@ -498,16 +503,16 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
-    public List<Group> findDynGroups(final User user) {
+    public List<Group> findDynGroups(final String key) {
         Query query = entityManager().createNativeQuery(
                 "SELECT group_id FROM " + JPAGroupDAO.UDYNMEMB_TABLE + " WHERE any_id=?");
-        query.setParameter(1, user.getKey());
+        query.setParameter(1, key);
 
         List<Group> result = new ArrayList<>();
-        for (Object key : query.getResultList()) {
-            String actualKey = key instanceof Object[]
-                    ? (String) ((Object[]) key)[0]
-                    : ((String) key);
+        for (Object resultKey : query.getResultList()) {
+            String actualKey = resultKey instanceof Object[]
+                    ? (String) ((Object[]) resultKey)[0]
+                    : ((String) resultKey);
 
             Group group = groupDAO().find(actualKey);
             if (group == null) {
@@ -530,7 +535,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                         return input.getRightEnd();
                     }
                 }, new ArrayList<Group>()),
-                findDynGroups(user));
+                findDynGroups(user.getKey()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
index fd40470..5cac5bb 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
@@ -135,6 +135,10 @@ class SearchSupport {
         return new SearchView("svdr", JPARoleDAO.DYNMEMB_TABLE);
     }
 
+    public SearchView dynrealmmembership() {
+        return new SearchView("svdrealm", JPADynRealmDAO.DYNMEMB_TABLE);
+    }
+
     public SearchView nullAttr() {
         return new SearchView("svna", field().name + "_null_attr");
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java
new file mode 100644
index 0000000..3a0aaa1
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import javax.persistence.Cacheable;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.jpa.validation.entity.DynRealmCheck;
+
+@Entity
+@Table(name = JPADynRealm.TABLE)
+@Cacheable
+@DynRealmCheck
+public class JPADynRealm extends AbstractProvidedKeyEntity implements DynRealm {
+
+    private static final long serialVersionUID = -6851035842423560341L;
+
+    public static final String TABLE = "DynRealm";
+
+    @NotNull
+    private String fiql;
+
+    @Override
+    public String getFIQLCond() {
+        return fiql;
+    }
+
+    @Override
+    public void setFIQLCond(final String fiql) {
+        this.fiql = fiql;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 7d9660c..daea67a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -124,6 +124,7 @@ import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAOrgUnit;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 
 @Component
 public class JPAEntityFactory implements EntityFactory {
@@ -137,6 +138,8 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPADomain();
         } else if (reference.equals(Realm.class)) {
             result = (E) new JPARealm();
+        } else if (reference.equals(DynRealm.class)) {
+            result = (E) new JPADynRealm();
         } else if (reference.equals(AnyTemplateRealm.class)) {
             result = (E) new JPAAnyTemplateRealm();
         } else if (reference.equals(AccountPolicy.class)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
index 919c3cb..30a1f47 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
@@ -41,6 +41,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.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.validation.entity.RoleCheck;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 
 @Entity
 @Table(name = JPARole.TABLE)
@@ -67,6 +68,14 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @Valid
     private List<JPARealm> realms = new ArrayList<>();
 
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(joinColumns =
+            @JoinColumn(name = "role_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "dynamicRealm_id"))
+    @Valid
+    private List<JPADynRealm> dynRealms = new ArrayList<>();
+
     @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "role")
     @Valid
     private JPADynRoleMembership dynMembership;
@@ -91,6 +100,17 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     }
 
     @Override
+    public boolean add(final DynRealm dynamicRealm) {
+        checkType(dynamicRealm, JPADynRealm.class);
+        return dynRealms.add((JPADynRealm) dynamicRealm);
+    }
+
+    @Override
+    public List<? extends DynRealm> getDynRealms() {
+        return dynRealms;
+    }
+
+    @Override
     public DynRoleMembership getDynMembership() {
         return dynMembership;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java
new file mode 100644
index 0000000..567dc74
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.validation.entity;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = DynRealmValidator.class)
+@Documented
+public @interface DynRealmCheck {
+
+    String message() default "{org.apache.syncope.core.persistence.validation.dynrealm}";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java
new file mode 100644
index 0000000..e97c122
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.validation.entity;
+
+import java.util.regex.Pattern;
+import javax.validation.ConstraintValidatorContext;
+import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+
+public class DynRealmValidator extends AbstractValidator<DynRealmCheck, DynRealm> {
+
+    private static final Pattern REALM_KEY_PATTERN = Pattern.compile("^[A-Za-z0-9]+");
+
+    @Override
+    public boolean isValid(final DynRealm dynRealm, final ConstraintValidatorContext context) {
+        context.disableDefaultConstraintViolation();
+
+        boolean isValid = true;
+
+        if (dynRealm.getKey().startsWith("/") || !REALM_KEY_PATTERN.matcher(dynRealm.getKey()).matches()) {
+            isValid = false;
+
+            context.buildConstraintViolationWithTemplate(
+                    getTemplate(EntityViolationType.InvalidDynRealm,
+                            "Only letters and numbers are allowed in dynamic realm key, and must not start with /")).
+                    addPropertyNode("key").addConstraintViolation();
+        }
+
+        return isValid;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
index e1bdf68..3b46f62 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
@@ -28,6 +28,7 @@ import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntit
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.entity.AnnotatedEntity;
 import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.DynMembership;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.GroupableRelatable;
 import org.apache.syncope.core.persistence.api.entity.Policy;
@@ -62,6 +63,7 @@ public class EntityValidationListener {
                         && !Policy.class.equals(interf)
                         && !GroupableRelatable.class.equals(interf)
                         && !Any.class.equals(interf)
+                        && !DynMembership.class.equals(interf)
                         && Entity.class.isAssignableFrom(interf)) {
 
                     entityInt = interf;

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/resources/indexes.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/indexes.xml b/core/persistence-jpa/src/main/resources/indexes.xml
index 1dfa63d..ded4d52 100644
--- a/core/persistence-jpa/src/main/resources/indexes.xml
+++ b/core/persistence-jpa/src/main/resources/indexes.xml
@@ -29,6 +29,9 @@ under the License.
   <entry key="DynRoleMembers_any_id">CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id)</entry>
   <entry key="DynRoleMembers_role_id">CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id)</entry>
 
+  <entry key="DynRealmMembers_any_id">CREATE INDEX DynRealmMembers_any_id ON DynRealmMembers(any_id)</entry>
+  <entry key="DynRealmMembers_realm_id">CREATE INDEX DynRealmMembers_dynRealm_id ON DynRealmMembers(dynRealm_id)</entry>
+
   <entry key="UPlainAttrValue_stringvalueIndex">CREATE INDEX UAttrValue_stringvalueIndex ON UPlainAttrValue(stringvalue)</entry>
   <entry key="UPlainAttrValue_datevalueIndex">CREATE INDEX UAttrValue_datevalueIndex ON UPlainAttrValue(datevalue)</entry>
   <entry key="UPlainAttrValue_longvalueIndex">CREATE INDEX UAttrValue_longvalueIndex ON UPlainAttrValue(longvalue)</entry>

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/resources/views.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/views.xml b/core/persistence-jpa/src/main/resources/views.xml
index fb68516..bcff6b9 100644
--- a/core/persistence-jpa/src/main/resources/views.xml
+++ b/core/persistence-jpa/src/main/resources/views.xml
@@ -39,6 +39,12 @@ under the License.
     role_id CHAR(36),
     UNIQUE(any_id, role_id))
   </entry>
+  <entry key="DynRealmMembers">
+    CREATE TABLE DynRealmMembers(
+    any_id CHAR(36),
+    dynRealm_id CHAR(36),
+    UNIQUE(any_id, dynRealm_id))
+  </entry>
 
   <!-- user -->
   <entry key="user_search">

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
index c7e5c62..1ba9913 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
@@ -109,7 +109,7 @@ public class AnySearchTest extends AbstractTest {
         assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(groupCond)));
 
         RoleCond roleCond = new RoleCond();
-        roleCond.setRoleKey("Other");
+        roleCond.setRole("Other");
         assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(roleCond)));
 
         user = userDAO.find("c9b2dec2-00a7-4855-97c0-d854842b4b24");
@@ -303,7 +303,7 @@ public class AnySearchTest extends AbstractTest {
     @Test
     public void searchByRole() {
         RoleCond roleCond = new RoleCond();
-        roleCond.setRoleKey("Other");
+        roleCond.setRole("Other");
 
         List<User> users = searchDAO.search(SearchCond.getLeafCond(roleCond), AnyTypeKind.USER);
         assertNotNull(users);

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java
index 174eef2..55fbb19 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.persistence.jpa.inner;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
index f91aa7e..f3d3203 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
@@ -100,7 +100,7 @@ public class AnySearchTest extends AbstractTest {
 
         // 2. search user by this dynamic role
         RoleCond roleCond = new RoleCond();
-        roleCond.setRoleKey(role.getKey());
+        roleCond.setRole(role.getKey());
 
         List<User> users = searchDAO.search(SearchCond.getLeafCond(roleCond), AnyTypeKind.USER);
         assertNotNull(users);

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java
new file mode 100644
index 0000000..307b865
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.outer;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class DynRealmTest extends AbstractTest {
+
+    @Autowired
+    private DynRealmDAO dynRealmDAO;
+
+    @Autowired
+    private AnySearchDAO searchDAO;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Test
+    public void misc() {
+        DynRealm dynRealm = entityFactory.newEntity(DynRealm.class);
+        dynRealm.setKey("/name");
+        dynRealm.setFIQLCond("cool==true");
+
+        // invalid key (starts with /)
+        try {
+            dynRealmDAO.save(dynRealm);
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        dynRealm.setKey("name");
+        DynRealm actual = dynRealmDAO.save(dynRealm);
+        assertNotNull(actual);
+
+        dynRealmDAO.flush();
+
+        DynRealmCond dynRealmCond = new DynRealmCond();
+        dynRealmCond.setDynRealm(actual.getKey());
+        List<User> matching = searchDAO.search(SearchCond.getLeafCond(dynRealmCond), AnyTypeKind.USER);
+        assertNotNull(matching);
+        assertFalse(matching.isEmpty());
+
+        User user = matching.get(0);
+        assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(dynRealmCond)));
+
+        assertTrue(userDAO.findDynRealms(user.getKey()).contains(actual.getKey()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java
new file mode 100644
index 0000000..5244b65
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.data;
+
+import org.apache.syncope.common.lib.to.DynRealmTO;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+
+public interface DynRealmDataBinder {
+
+    DynRealm create(DynRealmTO dynRealmTO);
+
+    DynRealm update(DynRealm dynRealm, DynRealmTO dynRealmTO);
+
+    DynRealmTO getDynRealmTO(DynRealm dynRealm);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
index 5428f5f..550f183 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
@@ -102,6 +102,9 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
                 details);
 
         if (details) {
+            // dynamic realms
+            anyObjectTO.getDynRealms().addAll(userDAO.findDynRealms(anyObject.getKey()));
+
             // relationships
             CollectionUtils.collect(anyObject.getRelationships(), new Transformer<ARelationship, RelationshipTO>() {
 
@@ -126,7 +129,7 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
             }, anyObjectTO.getMemberships());
 
             // dynamic memberships
-            CollectionUtils.collect(anyObjectDAO.findDynGroups(anyObject),
+            CollectionUtils.collect(anyObjectDAO.findDynGroups(anyObject.getKey()),
                     EntityUtils.<Group>keyTransformer(), anyObjectTO.getDynGroups());
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java
new file mode 100644
index 0000000..56d7903
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.DynRealmTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.provisioning.api.data.DynRealmDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DynRealmDataBinderImpl implements DynRealmDataBinder {
+
+    @Autowired
+    private DynRealmDAO dynRealmDAO;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Override
+    public DynRealm create(final DynRealmTO dynRealmTO) {
+        return update(entityFactory.newEntity(DynRealm.class), dynRealmTO);
+    }
+
+    @Override
+    public DynRealm update(final DynRealm dynRealm, final DynRealmTO dynRealmTO) {
+        dynRealm.setKey(dynRealmTO.getKey());
+
+        SearchCond cond = SearchCondConverter.convert(dynRealmTO.getCond());
+        if (!cond.isValid()) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
+            sce.getElements().add(dynRealmTO.getCond());
+            throw sce;
+        }
+        dynRealm.setFIQLCond(dynRealmTO.getCond());
+
+        return dynRealmDAO.save(dynRealm);
+    }
+
+    @Override
+    public DynRealmTO getDynRealmTO(final DynRealm dynRealm) {
+        DynRealmTO dynRealmTO = new DynRealmTO();
+
+        dynRealmTO.setKey(dynRealm.getKey());
+        dynRealmTO.setCond(dynRealm.getFIQLCond());
+
+        return dynRealmTO;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
index 32206aa..c4f8c50 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
@@ -231,7 +231,7 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
         }
 
         group = groupDAO.save(group);
-        
+
         // dynamic membership
         if (groupPatch.getUDynMembershipCond() == null) {
             if (group.getUDynMembership() != null) {
@@ -345,6 +345,11 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
                 group.getResources(),
                 details);
 
+        if (details) {
+            // dynamic realms
+            groupTO.getDynRealms().addAll(groupDAO.findDynRealms(group.getKey()));
+        }
+
         if (group.getUDynMembership() != null) {
             groupTO.setUDynMembershipCond(group.getUDynMembership().getFIQLCond());
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
index 6ccb229..b907051 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
@@ -23,15 +23,18 @@ import org.apache.commons.collections4.Transformer;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.RoleTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
 import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 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.DynRoleMembership;
 import org.apache.syncope.core.provisioning.api.data.RoleDataBinder;
+import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -46,6 +49,9 @@ public class RoleDataBinderImpl implements RoleDataBinder {
     private RealmDAO realmDAO;
 
     @Autowired
+    private DynRealmDAO dynRealmDAO;
+
+    @Autowired
     private RoleDAO roleDAO;
 
     @Autowired
@@ -93,6 +99,16 @@ public class RoleDataBinderImpl implements RoleDataBinder {
             }
         }
 
+        role.getDynRealms().clear();
+        for (String key : roleTO.getDynRealms()) {
+            DynRealm dynRealm = dynRealmDAO.find(key);
+            if (dynRealm == null) {
+                LOG.debug("Invalid dynamic ream {}, ignoring", key);
+            } else {
+                role.add(dynRealm);
+            }
+        }
+
         role = roleDAO.save(role);
 
         // dynamic membership
@@ -127,6 +143,8 @@ public class RoleDataBinderImpl implements RoleDataBinder {
             }
         }, roleTO.getRealms());
 
+        CollectionUtils.collect(role.getDynRealms(), EntityUtils.keyTransformer(), roleTO.getDynRealms());
+
         if (role.getDynMembership() != null) {
             roleTO.setDynMembershipCond(role.getDynMembership().getFIQLCond());
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index 3cd52c3..54c9983 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -606,6 +606,9 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
                 details);
 
         if (details) {
+            // dynamic realms
+            userTO.getDynRealms().addAll(userDAO.findDynRealms(user.getKey()));
+
             // roles
             CollectionUtils.collect(user.getRoles(),
                     EntityUtils.<Role>keyTransformer(), userTO.getRoles());
@@ -634,9 +637,9 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
             }, userTO.getMemberships());
 
             // dynamic memberships
-            CollectionUtils.collect(userDAO.findDynRoles(user),
+            CollectionUtils.collect(userDAO.findDynRoles(user.getKey()),
                     EntityUtils.<Role>keyTransformer(), userTO.getDynRoles());
-            CollectionUtils.collect(userDAO.findDynGroups(user),
+            CollectionUtils.collect(userDAO.findDynGroups(user.getKey()),
                     EntityUtils.<Group>keyTransformer(), userTO.getDynGroups());
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java
new file mode 100644
index 0000000..41e3bad
--- /dev/null
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import java.net.URI;
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.DynRealmTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.DynRealmService;
+import org.apache.syncope.core.logic.DynRealmLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class DynRealmServiceImpl extends AbstractServiceImpl implements DynRealmService {
+
+    @Autowired
+    private DynRealmLogic logic;
+
+    @Override
+    public List<DynRealmTO> list() {
+        return logic.list();
+    }
+
+    @Override
+    public DynRealmTO read(final String key) {
+        return logic.read(key);
+    }
+
+    @Override
+    public Response create(final DynRealmTO roleTO) {
+        DynRealmTO created = logic.create(roleTO);
+        URI location = uriInfo.getAbsolutePathBuilder().path(created.getKey()).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, created.getKey()).
+                build();
+    }
+
+    @Override
+    public void update(final DynRealmTO roleTO) {
+        logic.update(roleTO);
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
index aba2e50..79ba01b 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.core.spring.security;
 
 import com.fasterxml.jackson.core.type.TypeReference;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -29,11 +28,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.annotation.Resource;
-import org.apache.commons.collections4.Closure;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.SetUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.SyncopeConstants;
@@ -65,6 +63,7 @@ import org.apache.syncope.core.provisioning.api.ConnectorFactory;
 import org.apache.syncope.core.provisioning.api.EntitlementsHolder;
 import org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
 import org.identityconnectors.framework.common.objects.Uid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -92,6 +91,10 @@ public class AuthDataAccessor {
     protected static final Set<SyncopeGrantedAuthority> ANONYMOUS_AUTHORITIES =
             Collections.singleton(new SyncopeGrantedAuthority(StandardEntitlement.ANONYMOUS));
 
+    protected static final String[] GROUP_OWNER_ENTITLEMENTS = new String[] {
+        StandardEntitlement.GROUP_READ, StandardEntitlement.GROUP_UPDATE, StandardEntitlement.GROUP_DELETE
+    };
+
     @Resource(name = "adminUser")
     protected String adminUser;
 
@@ -282,39 +285,35 @@ public class AuthDataAccessor {
         if (user.isMustChangePassword()) {
             authorities.add(new SyncopeGrantedAuthority(StandardEntitlement.MUST_CHANGE_PASSWORD));
         } else {
-            final Map<String, Set<String>> entForRealms = new HashMap<>();
-
-            // Give entitlements as assigned by roles (with realms, where applicable) - assigned either
-            // statically and dynamically
-            for (final Role role : userDAO.findAllRoles(user)) {
-                IterableUtils.forEach(role.getEntitlements(), new Closure<String>() {
-
-                    @Override
-                    public void execute(final String entitlement) {
-                        Set<String> realms = entForRealms.get(entitlement);
-                        if (realms == null) {
-                            realms = new HashSet<>();
-                            entForRealms.put(entitlement, realms);
-                        }
+            Map<String, Set<String>> entForRealms = new HashMap<>();
+
+            // Give entitlements as assigned by roles (with static or dynamic realms, where applicable) - assigned
+            // either statically and dynamically
+            for (Role role : userDAO.findAllRoles(user)) {
+                for (String entitlement : role.getEntitlements()) {
+                    Set<String> realms = entForRealms.get(entitlement);
+                    if (realms == null) {
+                        realms = new HashSet<>();
+                        entForRealms.put(entitlement, realms);
+                    }
+
+                    CollectionUtils.collect(role.getRealms(), new Transformer<Realm, String>() {
 
-                        CollectionUtils.collect(role.getRealms(), new Transformer<Realm, String>() {
+                        @Override
+                        public String transform(final Realm realm) {
+                            return realm.getFullPath();
+                        }
+                    }, realms);
 
-                            @Override
-                            public String transform(final Realm realm) {
-                                return realm.getFullPath();
-                            }
-                        }, realms);
+                    if (!entitlement.endsWith("_CREATE") && !entitlement.endsWith("_DELETE")) {
+                        CollectionUtils.collect(role.getDynRealms(), EntityUtils.keyTransformer(), realms);
                     }
-                });
+                }
             }
 
             // Give group entitlements for owned groups
             for (Group group : groupDAO.findOwnedByUser(user.getKey())) {
-                for (String entitlement : Arrays.asList(
-                        StandardEntitlement.GROUP_READ,
-                        StandardEntitlement.GROUP_UPDATE,
-                        StandardEntitlement.GROUP_DELETE)) {
-
+                for (String entitlement : GROUP_OWNER_ENTITLEMENTS) {
                     Set<String> realms = entForRealms.get(entitlement);
                     if (realms == null) {
                         realms = new HashSet<>();
@@ -376,7 +375,7 @@ public class AuthDataAccessor {
                         + " for JWT " + authentication.getClaims().getTokenId());
             }
 
-            if (user.isSuspended() != null && user.isSuspended()) {
+            if (BooleanUtils.isTrue(user.isSuspended())) {
                 throw new DisabledException("User " + user.getUsername() + " is suspended");
             }
 
@@ -385,13 +384,9 @@ public class AuthDataAccessor {
                 throw new DisabledException("User " + user.getUsername() + " not allowed to authenticate");
             }
 
-            if (user.isMustChangePassword()) {
+            if (BooleanUtils.isTrue(user.isMustChangePassword())) {
                 authorities = Collections.singleton(
                         new SyncopeGrantedAuthority(StandardEntitlement.MUST_CHANGE_PASSWORD));
-            } else if (accessToken.getAuthorities() == null) {
-                LOG.debug("No authorities found in JWT, calculating...");
-
-                authorities = getUserAuthorities(user);
             } else {
                 LOG.debug("Authorities found in JWT, fetching...");
 
@@ -400,8 +395,8 @@ public class AuthDataAccessor {
                             ENCRYPTOR.decode(new String(accessToken.getAuthorities()), CipherAlgorithm.AES),
                             new TypeReference<Set<SyncopeGrantedAuthority>>() {
                     });
-                } catch (Exception e) {
-                    LOG.error("Could not read stored authorities", e);
+                } catch (Throwable t) {
+                    LOG.error("Could not read stored authorities", t);
                     authorities = Collections.emptySet();
                 }
             }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
index 47bcf9d..d9ce84f 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
@@ -103,7 +103,13 @@ public class ElasticsearchUtils {
                                 ? userDAO.findAllResourceKeys(any.getKey())
                                 : any instanceof AnyObject
                                         ? anyObjectDAO.findAllResourceKeys(any.getKey())
-                                        : any.getResourceKeys());
+                                        : any.getResourceKeys()).
+                field("dynRealms",
+                        any instanceof User
+                                ? userDAO.findDynRealms(any.getKey())
+                                : any instanceof AnyObject
+                                        ? anyObjectDAO.findDynRealms(any.getKey())
+                                        : groupDAO.findDynRealms(any.getKey()));
 
         if (any instanceof AnyObject) {
             AnyObject anyObject = ((AnyObject) any);

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
index 0318bc5..82b7c6f 100644
--- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
+++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
@@ -18,9 +18,12 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import static org.apache.syncope.core.persistence.jpa.dao.AbstractDAO.LOG;
+
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.apache.commons.collections4.CollectionUtils;
@@ -34,6 +37,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
@@ -44,6 +48,7 @@ import org.apache.syncope.core.persistence.api.dao.search.RoleCond;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.Realm;
@@ -75,21 +80,37 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
     @Autowired
     private ElasticsearchUtils elasticsearchUtils;
 
-    private DisMaxQueryBuilder adminRealmsFilter(final Set<String> adminRealms) {
+    private Pair<DisMaxQueryBuilder, Set<String>> adminRealmsFilter(final Set<String> adminRealms) {
         DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
 
+        Set<String> dynRealmKeys = new HashSet<>();
         for (String realmPath : RealmUtils.normalize(adminRealms)) {
-            Realm realm = realmDAO.findByFullPath(realmPath);
-            if (realm == null) {
-                LOG.warn("Ignoring invalid realm {}", realmPath);
+            if (realmPath.startsWith("/")) {
+                Realm realm = realmDAO.findByFullPath(realmPath);
+                if (realm == null) {
+                    LOG.warn("Ignoring invalid realm {}", realmPath);
+                } else {
+                    for (Realm descendant : realmDAO.findDescendants(realm)) {
+                        builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath()));
+                    }
+                }
             } else {
-                for (Realm descendant : realmDAO.findDescendants(realm)) {
-                    builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath()));
+                DynRealm dynRealm = dynRealmDAO.find(realmPath);
+                if (dynRealm == null) {
+                    LOG.warn("Ignoring invalid dynamic realm {}", realmPath);
+                } else {
+                    dynRealmKeys.add(dynRealm.getKey());
+                    builder.add(QueryBuilders.termQuery("dynRealm", dynRealm.getKey()));
                 }
             }
         }
+        if (!dynRealmKeys.isEmpty()) {
+            for (Realm descendant : realmDAO.findAll()) {
+                builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath()));
+            }
+        }
 
-        return builder;
+        return Pair.of(builder, dynRealmKeys);
     }
 
     private SearchRequestBuilder searchRequestBuilder(
@@ -97,14 +118,16 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
             final SearchCond cond,
             final AnyTypeKind kind) {
 
+        Pair<DisMaxQueryBuilder, Set<String>> filter = adminRealmsFilter(adminRealms);
+
         return client.prepareSearch(AuthContextUtils.getDomain().toLowerCase()).
                 setTypes(kind.name()).
                 setSearchType(SearchType.QUERY_THEN_FETCH).
                 setQuery(SyncopeConstants.FULL_ADMIN_REALMS.equals(adminRealms)
                         ? getQueryBuilder(cond, kind)
                         : QueryBuilders.boolQuery().
-                                must(adminRealmsFilter(adminRealms)).
-                                must(getQueryBuilder(cond, kind)));
+                                must(filter.getLeft()).
+                                must(getQueryBuilder(buildEffectiveCond(cond, filter.getRight()), kind)));
     }
 
     @Override
@@ -196,6 +219,8 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
                     builder = getQueryBuilder(cond.getAssignableCond());
                 } else if (cond.getRoleCond() != null && AnyTypeKind.USER == kind) {
                     builder = getQueryBuilder(cond.getRoleCond());
+                } else if (cond.getDynRealmCond() != null) {
+                    builder = getQueryBuilder(cond.getDynRealmCond());
                 } else if (cond.getMemberCond() != null && AnyTypeKind.GROUP == kind) {
                     builder = getQueryBuilder(cond.getMemberCond());
                 } else if (cond.getResourceCond() != null) {
@@ -286,7 +311,11 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
     }
 
     private QueryBuilder getQueryBuilder(final RoleCond cond) {
-        return QueryBuilders.termQuery("roles", cond.getRoleKey());
+        return QueryBuilders.termQuery("roles", cond.getRole());
+    }
+
+    private QueryBuilder getQueryBuilder(final DynRealmCond cond) {
+        return QueryBuilders.termQuery("dynRealms", cond.getDynRealm());
     }
 
     private QueryBuilder getQueryBuilder(final MemberCond cond) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 1ef0d32..f22a045 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -66,6 +66,7 @@ import org.apache.syncope.common.rest.api.service.CamelRouteService;
 import org.apache.syncope.common.rest.api.service.ConfigurationService;
 import org.apache.syncope.common.rest.api.service.ConnectorService;
 import org.apache.syncope.common.rest.api.service.DomainService;
+import org.apache.syncope.common.rest.api.service.DynRealmService;
 import org.apache.syncope.common.rest.api.service.LoggerService;
 import org.apache.syncope.common.rest.api.service.NotificationService;
 import org.apache.syncope.common.rest.api.service.PolicyService;
@@ -182,6 +183,8 @@ public abstract class AbstractITCase {
 
     protected static RoleService roleService;
 
+    protected static DynRealmService dynRealmService;
+
     protected static UserService userService;
 
     protected static UserSelfService userSelfService;
@@ -262,6 +265,7 @@ public abstract class AbstractITCase {
         realmService = adminClient.getService(RealmService.class);
         anyObjectService = adminClient.getService(AnyObjectService.class);
         roleService = adminClient.getService(RoleService.class);
+        dynRealmService = adminClient.getService(DynRealmService.class);
         userService = adminClient.getService(UserService.class);
         userSelfService = adminClient.getService(UserSelfService.class);
         userWorkflowService = adminClient.getService(UserWorkflowService.class);

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/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
new file mode 100644
index 0000000..e773716
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.fit.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.patch.AttrPatch;
+import org.apache.syncope.common.lib.patch.GroupPatch;
+import org.apache.syncope.common.lib.patch.StringPatchItem;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.DynRealmTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+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.StandardEntitlement;
+import org.apache.syncope.common.rest.api.beans.AnyQuery;
+import org.apache.syncope.common.rest.api.service.DynRealmService;
+import org.apache.syncope.common.rest.api.service.GroupService;
+import org.apache.syncope.common.rest.api.service.UserService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.Test;
+
+public class DynRealmITCase extends AbstractITCase {
+
+    @Test
+    public void misc() {
+        DynRealmTO dynRealm = null;
+        try {
+            dynRealm = new DynRealmTO();
+            dynRealm.setKey("/name" + getUUIDString());
+            dynRealm.setCond("cool==true");
+
+            // invalid key (starts with /)
+            try {
+                dynRealmService.create(dynRealm);
+                fail();
+            } catch (SyncopeClientException e) {
+                assertEquals(ClientExceptionType.InvalidDynRealm, e.getType());
+            }
+            dynRealm.setKey("name" + getUUIDString());
+
+            Response response = dynRealmService.create(dynRealm);
+            dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
+            assertNotNull(dynRealm);
+
+            PagedResult<UserTO> matching = userService.search(new AnyQuery.Builder().fiql("cool==true").build());
+            assertNotNull(matching);
+            assertNotEquals(0, matching.getSize());
+
+            UserTO user = matching.getResult().get(0);
+
+            assertTrue(user.getDynRealms().contains(dynRealm.getKey()));
+        } finally {
+            if (dynRealm != null) {
+                dynRealmService.delete(dynRealm.getKey());
+            }
+        }
+    }
+
+    @Test
+    public void delegatedAdmin() {
+        DynRealmTO dynRealm = null;
+        RoleTO role = null;
+        try {
+            // 1. create dynamic realm for all users and groups having resource-ldap assigned
+            dynRealm = new DynRealmTO();
+            dynRealm.setKey("LDAPLovers" + getUUIDString());
+            dynRealm.setCond("$resources==resource-ldap");
+
+            Response response = dynRealmService.create(dynRealm);
+            dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
+            assertNotNull(dynRealm);
+
+            // 2. create role for such dynamic realm
+            role = new RoleTO();
+            role.setKey("Administer LDAP" + getUUIDString());
+            role.getEntitlements().add(StandardEntitlement.USER_SEARCH);
+            role.getEntitlements().add(StandardEntitlement.USER_READ);
+            role.getEntitlements().add(StandardEntitlement.USER_UPDATE);
+            role.getEntitlements().add(StandardEntitlement.GROUP_READ);
+            role.getEntitlements().add(StandardEntitlement.GROUP_UPDATE);
+            role.getDynRealms().add(dynRealm.getKey());
+
+            role = createRole(role);
+            assertNotNull(role);
+
+            // 3. assign the new role to vivaldi
+            UserPatch userPatch = new UserPatch();
+            userPatch.setKey("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee");
+            userPatch.getRoles().add(new StringPatchItem.Builder().value(role.getKey()).build());
+            UserTO vivaldi = updateUser(userPatch).getEntity();
+            assertNotNull(vivaldi);
+            assertTrue(vivaldi.getRoles().contains(role.getKey()));
+
+            // 4. create new user and group, assign resource-ldap
+            UserTO user = UserITCase.getUniqueSampleTO("dynRealmUser@apache.org");
+            user.setRealm("/even/two");
+            user.getResources().clear();
+            user.getResources().add(RESOURCE_NAME_LDAP);
+            user = createUser(user).getEntity();
+            assertNotNull(user);
+            final String userKey = user.getKey();
+
+            GroupTO group = GroupITCase.getSampleTO("dynRealmGroup");
+            group.setRealm("/odd");
+            group.getResources().clear();
+            group.getResources().add(RESOURCE_NAME_LDAP);
+            group = createGroup(group).getEntity();
+            assertNotNull(group);
+            final String groupKey = group.getKey();
+
+            // 5. verify that the new user and group are found when searching by dynamic realm
+            PagedResult<UserTO> matchingUsers = userService.search(new AnyQuery.Builder().realm("/").fiql(
+                    SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
+            assertTrue(IterableUtils.matchesAny(matchingUsers.getResult(), new Predicate<UserTO>() {
+
+                @Override
+                public boolean evaluate(final UserTO object) {
+                    return object.getKey().equals(userKey);
+                }
+            }));
+            PagedResult<GroupTO> matchingGroups = groupService.search(new AnyQuery.Builder().realm("/").fiql(
+                    SyncopeClient.getGroupSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
+            assertTrue(IterableUtils.matchesAny(matchingGroups.getResult(), new Predicate<GroupTO>() {
+
+                @Override
+                public boolean evaluate(final GroupTO object) {
+                    return object.getKey().equals(groupKey);
+                }
+            }));
+
+            // 6. perpare to act as vivaldi
+            SyncopeClient vivaldiClient = clientFactory.create("vivaldi", ADMIN_PWD);
+            UserService vivaldiUserService = vivaldiClient.getService(UserService.class);
+            GroupService vivaldiGroupService = vivaldiClient.getService(GroupService.class);
+
+            // 7. verify delegated administration
+            // USER_READ
+            assertNotNull(vivaldiUserService.read(userKey));
+
+            // GROUP_READ
+            assertNotNull(vivaldiGroupService.read(groupKey));
+
+            // USER_SEARCH
+            matchingUsers = vivaldiUserService.search(new AnyQuery.Builder().realm("/").build());
+            assertTrue(IterableUtils.matchesAny(matchingUsers.getResult(), new Predicate<UserTO>() {
+
+                @Override
+                public boolean evaluate(final UserTO object) {
+                    return object.getKey().equals(userKey);
+                }
+            }));
+
+            // USER_UPDATE
+            userPatch = new UserPatch();
+            userPatch.setKey(userKey);
+            userPatch.getResources().add(new StringPatchItem.Builder().value(RESOURCE_NAME_NOPROPAGATION).build());
+            user = vivaldiUserService.update(userPatch).
+                    readEntity(new GenericType<ProvisioningResult<UserTO>>() {
+                    }).getEntity();
+            assertNotNull(user);
+            assertTrue(user.getResources().contains(RESOURCE_NAME_NOPROPAGATION));
+
+            // GROUP_UPDATE
+            GroupPatch groupPatch = new GroupPatch();
+            groupPatch.setKey(groupKey);
+            groupPatch.getPlainAttrs().add(new AttrPatch.Builder().attrTO(attrTO("icon", "modified")).build());
+            group = vivaldiGroupService.update(groupPatch).
+                    readEntity(new GenericType<ProvisioningResult<GroupTO>>() {
+                    }).getEntity();
+            assertNotNull(group);
+            assertEquals("modified", group.getPlainAttrMap().get("icon").getValues().get(0));
+        } finally {
+            if (role != null) {
+                roleService.delete(role.getKey());
+            }
+            if (dynRealm != null) {
+                dynRealmService.delete(dynRealm.getKey());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java
index 8a8e6a2..9db9031 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java
@@ -125,7 +125,7 @@ public class ExceptionMapperITCase extends AbstractITCase {
             fail();
         } catch (Exception e) {
             String message = ERROR_MESSAGES.getProperty("errMessage.UniqueConstraintViolation");
-            assertEquals(e.getMessage(), "DataIntegrityViolation [" + message + "]");
+            assertEquals("EntityExists [" + message + "]", e.getMessage());
         }
     }
 


[4/4] syncope git commit: [SYNCOPE-1067] Core modifications

Posted by il...@apache.org.
[SYNCOPE-1067] Core modifications


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

Branch: refs/heads/master
Commit: a1bb672317e9bbd605a19e89272456b3ca8066f6
Parents: 6816f17
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Jun 8 17:54:45 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Jun 8 17:54:58 2017 +0200

----------------------------------------------------------------------
 .../console/panels/RoleDirectoryPanel.java      |   2 +
 .../panels/RoleDirectoryPanel.properties        |   1 +
 .../panels/RoleDirectoryPanel_it.properties     |   1 +
 .../panels/RoleDirectoryPanel_pt_BR.properties  |   1 +
 .../panels/RoleDirectoryPanel_ru.properties     |   1 +
 .../AbstractFiqlSearchConditionBuilder.java     |  25 +++
 .../syncope/common/lib/search/SpecialAttr.java  |   4 +
 .../common/lib/search/SyncopeProperty.java      |   4 +
 .../search/UserFiqlSearchConditionBuilder.java  |  12 --
 .../org/apache/syncope/common/lib/to/AnyTO.java |   9 +
 .../syncope/common/lib/to/DynRealmTO.java       |  55 +++++
 .../apache/syncope/common/lib/to/RoleTO.java    |   9 +
 .../common/lib/types/ClientExceptionType.java   |   1 +
 .../common/lib/types/EntityViolationType.java   |   1 +
 .../common/lib/types/StandardEntitlement.java   |  10 +
 .../rest/api/service/DynRealmService.java       |  90 ++++++++
 .../syncope/core/logic/AbstractAnyLogic.java    |  42 +++-
 .../syncope/core/logic/AnyObjectLogic.java      |   4 -
 .../syncope/core/logic/DynRealmLogic.java       | 127 +++++++++++
 .../apache/syncope/core/logic/GroupLogic.java   |   8 -
 .../apache/syncope/core/logic/UserLogic.java    |   8 -
 .../core/persistence/api/dao/AnyDAO.java        |   1 +
 .../core/persistence/api/dao/AnyObjectDAO.java  |   2 +-
 .../core/persistence/api/dao/DynRealmDAO.java   |  41 ++++
 .../core/persistence/api/dao/RoleDAO.java       |   2 +-
 .../core/persistence/api/dao/UserDAO.java       |   4 +-
 .../api/dao/search/DynRealmCond.java            |  39 ++++
 .../persistence/api/dao/search/RoleCond.java    |  12 +-
 .../persistence/api/dao/search/SearchCond.java  |  25 ++-
 .../core/persistence/api/entity/DynRealm.java   |  23 ++
 .../core/persistence/api/entity/Role.java       |   4 +
 .../api/search/SearchCondVisitor.java           |   9 +-
 .../api/search/SearchCondConverterTest.java     |  20 +-
 core/persistence-jpa/pom.xml                    |  18 ++
 .../persistence/jpa/dao/AbstractAnyDAO.java     |  39 ++++
 .../jpa/dao/AbstractAnySearchDAO.java           |  22 ++
 .../persistence/jpa/dao/JPAAnyObjectDAO.java    |  19 +-
 .../persistence/jpa/dao/JPAAnySearchDAO.java    |  74 +++++--
 .../persistence/jpa/dao/JPADynRealmDAO.java     | 162 ++++++++++++++
 .../core/persistence/jpa/dao/JPAGroupDAO.java   |  16 +-
 .../core/persistence/jpa/dao/JPARoleDAO.java    |   6 +-
 .../core/persistence/jpa/dao/JPAUserDAO.java    |  37 ++--
 .../core/persistence/jpa/dao/SearchSupport.java |   4 +
 .../persistence/jpa/entity/JPADynRealm.java     |  50 +++++
 .../jpa/entity/JPAEntityFactory.java            |   3 +
 .../core/persistence/jpa/entity/JPARole.java    |  20 ++
 .../jpa/validation/entity/DynRealmCheck.java    |  41 ++++
 .../validation/entity/DynRealmValidator.java    |  47 +++++
 .../entity/EntityValidationListener.java        |   2 +
 .../src/main/resources/indexes.xml              |   3 +
 .../src/main/resources/views.xml                |   6 +
 .../persistence/jpa/inner/AnySearchTest.java    |   4 +-
 .../core/persistence/jpa/inner/RoleTest.java    |   1 -
 .../persistence/jpa/outer/AnySearchTest.java    |   2 +-
 .../persistence/jpa/outer/DynRealmTest.java     |  84 ++++++++
 .../api/data/DynRealmDataBinder.java            |  31 +++
 .../java/data/AnyObjectDataBinderImpl.java      |   5 +-
 .../java/data/DynRealmDataBinderImpl.java       |  72 +++++++
 .../java/data/GroupDataBinderImpl.java          |   7 +-
 .../java/data/RoleDataBinderImpl.java           |  18 ++
 .../java/data/UserDataBinderImpl.java           |   7 +-
 .../rest/cxf/service/DynRealmServiceImpl.java   |  66 ++++++
 .../core/spring/security/AuthDataAccessor.java  |  69 +++---
 .../client/ElasticsearchUtils.java              |   8 +-
 .../jpa/dao/ElasticsearchAnySearchDAO.java      |  49 ++++-
 .../org/apache/syncope/fit/AbstractITCase.java  |   4 +
 .../apache/syncope/fit/core/DynRealmITCase.java | 211 +++++++++++++++++++
 .../syncope/fit/core/ExceptionMapperITCase.java |   2 +-
 68 files changed, 1652 insertions(+), 154 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
index d911887..1251b6d 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
@@ -115,6 +115,8 @@ public class RoleDirectoryPanel extends DirectoryPanel<RoleTO, RoleWrapper, Role
                 new ResourceModel("entitlements", "Entitlements"), null, "entitlements"));
         columns.add(new PropertyColumn<RoleTO, String>(
                 new ResourceModel("realms"), null, "realms"));
+        columns.add(new PropertyColumn<RoleTO, String>(
+                new ResourceModel("dynRealms"), null, "dynRealms"));
 
         return columns;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel.properties
index 3a27730..d656ca5 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel.properties
@@ -17,3 +17,4 @@
 any.edit=Edit role ${roleTO.key}
 any.new=New role
 role.members=Role '${key}' members
+dynRealms=Dynamic Realms

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_it.properties
index 04a466e..34ba4ef 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_it.properties
@@ -17,3 +17,4 @@
 any.edit=Modifica ruolo ${roleTO.key}
 any.new=Nuovo ruolo
 role.members=Utenti membri del rulo '${key}'
+dynRealms=Realm dinamici

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_pt_BR.properties
index 1de6e43..4e55e7e 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_pt_BR.properties
@@ -17,3 +17,4 @@
 any.edit=Alterar fun\u00e7\u00e3o ${roleTO.key}
 any.new=Novo fun\u00e7\u00e3o
 role.members=Role '${key}' members
+dynRealms=Dynamic Realms

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_ru.properties
index b292ea7..61dd821 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_ru.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_ru.properties
@@ -18,3 +18,4 @@
 any.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u043e\u043b\u044c ${roleTO.key}
 any.new=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0440\u043e\u043b\u044c
 role.members=\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u0440\u043e\u043b\u0438 '${key}'
+dynRealms=Dynamic Realms

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
index 3338c1b..18fee59 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
@@ -65,6 +65,18 @@ public abstract class AbstractFiqlSearchConditionBuilder extends FiqlSearchCondi
         return newBuilderInstance().is(property).notNullValue();
     }
 
+    public CompleteCondition inDynRealms(final String dynRealm, final String... moreDynRealms) {
+        return newBuilderInstance().
+                is(SpecialAttr.DYNREALMS.toString()).
+                inDynRealms(dynRealm, moreDynRealms);
+    }
+
+    public CompleteCondition notInDynRealms(final String dynRealm, final String... moreDynRealms) {
+        return newBuilderInstance().
+                is(SpecialAttr.DYNREALMS.toString()).
+                notInDynRealms(dynRealm, moreDynRealms);
+    }
+
     public CompleteCondition hasResources(final String resource, final String... moreResources) {
         return newBuilderInstance().is(SpecialAttr.RESOURCES.toString()).hasResources(resource, moreResources);
     }
@@ -122,5 +134,18 @@ public abstract class AbstractFiqlSearchConditionBuilder extends FiqlSearchCondi
         public CompleteCondition notEqualTolIgnoreCase(final String literalOrPattern) {
             return condition(SyncopeFiqlParser.NIEQ, literalOrPattern);
         }
+
+        @Override
+        public CompleteCondition inDynRealms(final String dynRealm, final String... moreDynRealms) {
+            this.result = SpecialAttr.DYNREALMS.toString();
+            return condition(FiqlParser.EQ, dynRealm, (Object[]) moreDynRealms);
+        }
+
+        @Override
+        public CompleteCondition notInDynRealms(final String dynRealm, final String... moreDynRealms) {
+            this.result = SpecialAttr.DYNREALMS.toString();
+            return condition(FiqlParser.NEQ, dynRealm, (Object[]) moreDynRealms);
+        }
+
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
index d8b6672..c744169 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
@@ -56,6 +56,10 @@ public enum SpecialAttr {
      */
     ROLES("$roles"),
     /**
+     * Applies to users, groups and any objects.
+     */
+    DYNREALMS("$dynRealms"),
+    /**
      * Applies to groups.
      */
     MEMBER("$member"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
index 0d06c5b..fe2d47e 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
@@ -70,4 +70,8 @@ public interface SyncopeProperty extends Property {
      */
     CompleteCondition hasNotResources(String resource, String... moreResources);
 
+    CompleteCondition inDynRealms(String dynRealm, String... moreDynRealms);
+
+    CompleteCondition notInDynRealms(String dynRealm, String... moreDynRealms);
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
index 89d81c7..8cd7421 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
@@ -88,18 +88,6 @@ public class UserFiqlSearchConditionBuilder extends AbstractFiqlSearchConditionB
                 notInRoles(role, moreRoles);
     }
 
-    @Override
-    public CompleteCondition hasResources(final String resource, final String... moreResources) {
-        return newBuilderInstance().
-                is(SpecialAttr.RESOURCES.toString()).
-                hasResources(resource, moreResources);
-    }
-
-    @Override
-    public CompleteCondition hasNotResources(final String resource, final String... moreResources) {
-        return newBuilderInstance().is(SpecialAttr.RESOURCES.toString()).hasNotResources(resource, moreResources);
-    }
-
     protected static class Builder extends AbstractFiqlSearchConditionBuilder.Builder
             implements UserProperty, CompleteCondition {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
index b63d4b0..47f1d9b 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
@@ -46,6 +46,8 @@ public abstract class AnyTO extends AbstractAnnotatedBean implements EntityTO, A
 
     private String realm;
 
+    private List<String> dynRealms = new ArrayList<>();
+
     private String status;
 
     private final List<String> auxClasses = new ArrayList<>();
@@ -84,6 +86,13 @@ public abstract class AnyTO extends AbstractAnnotatedBean implements EntityTO, A
         this.realm = realm;
     }
 
+    @XmlElementWrapper(name = "dynRealms")
+    @XmlElement(name = "dynRealmF")
+    @JsonProperty("dynRealms")
+    public List<String> getDynRealms() {
+        return dynRealms;
+    }
+
     public String getStatus() {
         return status;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java
new file mode 100644
index 0000000..c218ec6
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.to;
+
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "dynRealm")
+@XmlType
+public class DynRealmTO extends AbstractBaseBean implements EntityTO {
+
+    private static final long serialVersionUID = 4560822655754800031L;
+
+    private String key;
+
+    private String cond;
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @PathParam("key")
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    public String getCond() {
+        return cond;
+    }
+
+    public void setCond(final String cond) {
+        this.cond = cond;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
index cbb8784..9313349 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
@@ -42,6 +42,8 @@ public class RoleTO extends AbstractBaseBean implements EntityTO {
 
     private final List<String> realms = new ArrayList<>();
 
+    private final List<String> dynRealms = new ArrayList<>();
+
     private String dynMembershipCond;
 
     @Override
@@ -69,6 +71,13 @@ public class RoleTO extends AbstractBaseBean implements EntityTO {
         return realms;
     }
 
+    @XmlElementWrapper(name = "dynRealms")
+    @XmlElement(name = "dynRealm")
+    @JsonProperty("dynRealms")
+    public List<String> getDynRealms() {
+        return dynRealms;
+    }
+
     public String getDynMembershipCond() {
         return dynMembershipCond;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
index 9954b9b..1927737 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
@@ -58,6 +58,7 @@ public enum ClientExceptionType {
     InvalidMapping(Response.Status.BAD_REQUEST),
     InvalidMembership(Response.Status.BAD_REQUEST),
     InvalidRealm(Response.Status.BAD_REQUEST),
+    InvalidDynRealm(Response.Status.BAD_REQUEST),
     InvalidRole(Response.Status.BAD_REQUEST),
     InvalidUser(Response.Status.BAD_REQUEST),
     InvalidExternalResource(Response.Status.BAD_REQUEST),

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
index 3107536..6cc7e7c 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
@@ -35,6 +35,7 @@ public enum EntityViolationType {
     InvalidPolicy("org.apache.syncope.core.persistence.validation.policy"),
     InvalidPropagationTask("org.apache.syncope.core.persistence.validation.propagationtask"),
     InvalidRealm("org.apache.syncope.core.persistence.validation.realm"),
+    InvalidDynRealm("org.apache.syncope.core.persistence.validation.dynrealm"),
     InvalidReport("org.apache.syncope.core.persistence.validation.report"),
     InvalidResource("org.apache.syncope.core.persistence.validation.externalresource"),
     InvalidGroupOwner("org.apache.syncope.core.persistence.validation.group.owner"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
index aa3fd17..81709f6 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
@@ -78,6 +78,16 @@ public final class StandardEntitlement {
 
     public static final String ROLE_DELETE = "ROLE_DELETE";
 
+    public static final String DYNREALM_LIST = "DYNREALM_LIST";
+
+    public static final String DYNREALM_CREATE = "DYNREALM_CREATE";
+
+    public static final String DYNREALM_READ = "DYNREALM_READ";
+
+    public static final String DYNREALM_UPDATE = "DYNREALM_UPDATE";
+
+    public static final String DYNREALM_DELETE = "DYNREALM_DELETE";
+
     public static final String SCHEMA_LIST = "SCHEMA_LIST";
 
     public static final String SCHEMA_CREATE = "SCHEMA_CREATE";

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/DynRealmService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/DynRealmService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/DynRealmService.java
new file mode 100644
index 0000000..dc9836d
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/DynRealmService.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.DynRealmTO;
+
+/**
+ * REST operations for dynamic realms.
+ */
+@Path("dynRealms")
+public interface DynRealmService extends JAXRSService {
+
+    /**
+     * Returns a list of all dynamic realms.
+     *
+     * @return list of all dynamic realms.
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    List<DynRealmTO> list();
+
+    /**
+     * Returns dynamic realm with matching key.
+     *
+     * @param key dynamic realm key to be read
+     * @return dynamic realm with matching key
+     */
+    @GET
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    DynRealmTO read(@NotNull @PathParam("key") String key);
+
+    /**
+     * Creates a new dynamic realm.
+     *
+     * @param dynDynRealmTO dynamic realm to be created
+     * @return Response object featuring Location header of created dynamic realm
+     */
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    Response create(@NotNull DynRealmTO dynDynRealmTO);
+
+    /**
+     * Updates the dynamic realm matching the provided key.
+     *
+     * @param dynDynRealmTO dynamic realm to be stored
+     */
+    @PUT
+    @Path("{key}")
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    void update(@NotNull DynRealmTO dynDynRealmTO);
+
+    /**
+     * Deletes the dynamic realm matching the provided key.
+     *
+     * @param key dynamic realm key to be deleted
+     */
+    @DELETE
+    @Path("{key}")
+    void delete(@NotNull @PathParam("key") String key);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 003058f..066b3d2 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
@@ -38,12 +38,16 @@ import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
 import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
 import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
@@ -55,6 +59,15 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
 public abstract class AbstractAnyLogic<TO extends AnyTO, P extends AnyPatch> extends AbstractResourceAssociator<TO> {
 
     @Autowired
+    protected UserDAO userDAO;
+
+    @Autowired
+    protected GroupDAO groupDAO;
+
+    @Autowired
+    protected AnyObjectDAO anyObjectDAO;
+
+    @Autowired
     private RealmDAO realmDAO;
 
     @Autowired
@@ -191,9 +204,7 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P extends AnyPatch> ext
 
     }
 
-    protected Set<String> getEffectiveRealms(
-            final Set<String> allowedRealms, final String requestedRealm) {
-
+    protected Set<String> getEffectiveRealms(final Set<String> allowedRealms, final String requestedRealm) {
         Set<String> allowed = RealmUtils.normalize(allowedRealms);
         Set<String> requested = new HashSet<>();
         requested.add(requestedRealm);
@@ -202,18 +213,35 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P extends AnyPatch> ext
         CollectionUtils.select(requested, new StartsWithPredicate(allowed), effective);
         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);
+
         return effective;
     }
 
     protected void securityChecks(final Set<String> effectiveRealms, final String realm, final String key) {
-        if (!IterableUtils.matchesAny(effectiveRealms, new Predicate<String>() {
+        boolean authorized = IterableUtils.matchesAny(effectiveRealms, new Predicate<String>() {
 
             @Override
             public boolean evaluate(final String ownedRealm) {
                 return realm.startsWith(ownedRealm);
             }
-        })) {
-
+        });
+        if (!authorized) {
+            AnyDAO<?> anyDAO = this instanceof UserLogic
+                    ? userDAO
+                    : this instanceof GroupLogic
+                            ? groupDAO
+                            : anyObjectDAO;
+            authorized = !CollectionUtils.intersection(anyDAO.findDynRealms(key), effectiveRealms).isEmpty();
+        }
+        if (!authorized) {
             throw new DelegatedAdministrationException(
                     this instanceof UserLogic
                             ? AnyTypeKind.USER
@@ -225,7 +253,7 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P extends AnyPatch> ext
     }
 
     public abstract Date findLastChange(String key);
-    
+
     public abstract TO read(String key);
 
     public abstract int count(String realm);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 b4779df..c585dd0 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
@@ -39,7 +39,6 @@ import org.apache.syncope.common.lib.types.AnyEntitlement;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.PatchOperation;
-import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 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.dao.search.OrderByClause;
@@ -62,9 +61,6 @@ import org.springframework.transaction.annotation.Transactional;
 public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectPatch> {
 
     @Autowired
-    protected AnyObjectDAO anyObjectDAO;
-
-    @Autowired
     protected AnySearchDAO searchDAO;
 
     @Autowired

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/logic/src/main/java/org/apache/syncope/core/logic/DynRealmLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/DynRealmLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/DynRealmLogic.java
new file mode 100644
index 0000000..bed7028
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/DynRealmLogic.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.to.DynRealmTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.provisioning.api.data.DynRealmDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DynRealmLogic extends AbstractTransactionalLogic<DynRealmTO> {
+
+    @Autowired
+    private DynRealmDataBinder binder;
+
+    @Autowired
+    private DynRealmDAO dynRealmDAO;
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.DYNREALM_READ + "')")
+    public DynRealmTO read(final String key) {
+        DynRealm dynRealm = dynRealmDAO.find(key);
+        if (dynRealm == null) {
+            LOG.error("Could not find dynamic realm '" + key + "'");
+
+            throw new NotFoundException(key);
+        }
+
+        return binder.getDynRealmTO(dynRealm);
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.DYNREALM_LIST + "')")
+    public List<DynRealmTO> list() {
+        return CollectionUtils.collect(dynRealmDAO.findAll(), new Transformer<DynRealm, DynRealmTO>() {
+
+            @Override
+            public DynRealmTO transform(final DynRealm input) {
+                return binder.getDynRealmTO(input);
+            }
+        }, new ArrayList<DynRealmTO>());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.DYNREALM_CREATE + "')")
+    public DynRealmTO create(final DynRealmTO dynRealmTO) {
+        return binder.getDynRealmTO(dynRealmDAO.save(binder.create(dynRealmTO)));
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.DYNREALM_UPDATE + "')")
+    public DynRealmTO update(final DynRealmTO dynRealmTO) {
+        DynRealm dynRealm = dynRealmDAO.find(dynRealmTO.getKey());
+        if (dynRealm == null) {
+            LOG.error("Could not find dynamic realm '" + dynRealmTO.getKey() + "'");
+            throw new NotFoundException(dynRealmTO.getKey());
+        }
+
+        return binder.getDynRealmTO(dynRealmDAO.save(binder.update(dynRealm, dynRealmTO)));
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.DYNREALM_DELETE + "')")
+    public DynRealmTO delete(final String key) {
+        DynRealm dynRealm = dynRealmDAO.find(key);
+        if (dynRealm == null) {
+            LOG.error("Could not find dynamic realm '" + key + "'");
+
+            throw new NotFoundException(key);
+        }
+
+        DynRealmTO deleted = binder.getDynRealmTO(dynRealm);
+        dynRealmDAO.delete(key);
+        return deleted;
+    }
+
+    @Override
+    protected DynRealmTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        String key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof String) {
+                    key = (String) args[i];
+                } else if (args[i] instanceof DynRealmTO) {
+                    key = ((DynRealmTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if (key != null) {
+            try {
+                return binder.getDynRealmTO(dynRealmDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 b93142d..1ddc108 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
@@ -49,10 +49,8 @@ import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
@@ -84,12 +82,6 @@ import org.springframework.transaction.annotation.Transactional;
 public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupPatch> {
 
     @Autowired
-    protected GroupDAO groupDAO;
-
-    @Autowired
-    protected UserDAO userDAO;
-
-    @Autowired
     protected AnySearchDAO searchDAO;
 
     @Autowired

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 7cc5414..02d88cb 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
@@ -45,9 +45,7 @@ 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.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -70,12 +68,6 @@ import org.springframework.transaction.annotation.Transactional;
 public class UserLogic extends AbstractAnyLogic<UserTO, UserPatch> {
 
     @Autowired
-    protected UserDAO userDAO;
-
-    @Autowired
-    protected GroupDAO groupDAO;
-
-    @Autowired
     protected AnySearchDAO searchDAO;
 
     @Autowired

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
index 979e39b..75b0411 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
@@ -93,4 +93,5 @@ public interface AnyDAO<A extends Any<?>> extends DAO<A> {
 
     void delete(A any);
 
+    List<String> findDynRealms(String key);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
index 448aca6..8a10f0b 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
@@ -43,7 +43,7 @@ public interface AnyObjectDAO extends AnyDAO<AnyObject> {
 
     AnyObject authFindByName(String name);
 
-    List<Group> findDynGroups(AnyObject anyObject);
+    List<Group> findDynGroups(String key);
 
     List<ARelationship> findAllRelationships(AnyObject anyObject);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DynRealmDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DynRealmDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DynRealmDAO.java
new file mode 100644
index 0000000..a782ea8
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DynRealmDAO.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+
+public interface DynRealmDAO extends DAO<DynRealm> {
+
+    DynRealm find(String key);
+
+    List<DynRealm> findAll();
+
+    DynRealm save(DynRealm dynRealm);
+
+    void delete(String key);
+
+    void clearDynMembers(DynRealm dynRealm);
+
+    void refreshDynMemberships(Any<?> any);
+
+    void removeDynMemberships(String key);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java
index f76b382..cc29852 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java
@@ -45,6 +45,6 @@ public interface RoleDAO extends DAO<Role> {
 
     void refreshDynMemberships(User user);
 
-    void removeDynMemberships(User user);
+    void removeDynMemberships(String key);
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
index 9e44bb3..2a5d76d 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
@@ -42,11 +42,11 @@ public interface UserDAO extends AnyDAO<User> {
 
     List<User> findBySecurityQuestion(SecurityQuestion securityQuestion);
 
-    List<Role> findDynRoles(User user);
+    List<Role> findDynRoles(String key);
 
     Collection<Role> findAllRoles(User user);
 
-    List<Group> findDynGroups(User user);
+    List<Group> findDynGroups(String key);
 
     Collection<Group> findAllGroups(User user);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/DynRealmCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/DynRealmCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/DynRealmCond.java
new file mode 100644
index 0000000..e3f3590
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/DynRealmCond.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao.search;
+
+public class DynRealmCond extends AbstractSearchCond {
+
+    private static final long serialVersionUID = 9110213614796095482L;
+
+    private String dynRealm;
+
+    public String getDynRealm() {
+        return dynRealm;
+    }
+
+    public void setDynRealm(final String dynRealm) {
+        this.dynRealm = dynRealm;
+    }
+
+    @Override
+    public final boolean isValid() {
+        return dynRealm != null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RoleCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RoleCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RoleCond.java
index 0ee7648..08972fa 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RoleCond.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RoleCond.java
@@ -22,18 +22,18 @@ public class RoleCond extends AbstractSearchCond {
 
     private static final long serialVersionUID = 3581958527829522490L;
 
-    private String roleKey;
+    private String role;
 
-    public String getRoleKey() {
-        return roleKey;
+    public String getRole() {
+        return role;
     }
 
-    public void setRoleKey(final String roleKey) {
-        this.roleKey = roleKey;
+    public void setRole(final String role) {
+        this.role = role;
     }
 
     @Override
     public final boolean isValid() {
-        return roleKey != null;
+        return role != null;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
index e87df92..520fc58 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
@@ -49,6 +49,8 @@ public class SearchCond extends AbstractSearchCond {
 
     private RoleCond roleCond;
 
+    private DynRealmCond dynRealmCond;
+
     private ResourceCond resourceCond;
 
     private AssignableCond assignableCond;
@@ -117,6 +119,15 @@ public class SearchCond extends AbstractSearchCond {
         return nodeCond;
     }
 
+    public static SearchCond getLeafCond(final DynRealmCond dynRealmCond) {
+        SearchCond nodeCond = new SearchCond();
+
+        nodeCond.type = Type.LEAF;
+        nodeCond.dynRealmCond = dynRealmCond;
+
+        return nodeCond;
+    }
+
     public static SearchCond getLeafCond(final ResourceCond resourceCond) {
         SearchCond nodeCond = new SearchCond();
 
@@ -196,7 +207,9 @@ public class SearchCond extends AbstractSearchCond {
     }
 
     public static SearchCond getAndCond(final List<SearchCond> conditions) {
-        if (conditions.size() > 2) {
+        if (conditions.size() == 1) {
+            return conditions.get(0);
+        } else if (conditions.size() > 2) {
             SearchCond removed = conditions.remove(0);
             return getAndCond(removed, getAndCond(conditions));
         } else {
@@ -215,7 +228,9 @@ public class SearchCond extends AbstractSearchCond {
     }
 
     public static SearchCond getOrCond(final List<SearchCond> conditions) {
-        if (conditions.size() > 2) {
+        if (conditions.size() == 1) {
+            return conditions.get(0);
+        } else if (conditions.size() > 2) {
             SearchCond removed = conditions.remove(0);
             return getOrCond(removed, getOrCond(conditions));
         } else {
@@ -291,6 +306,10 @@ public class SearchCond extends AbstractSearchCond {
         return roleCond;
     }
 
+    public DynRealmCond getDynRealmCond() {
+        return dynRealmCond;
+    }
+
     public ResourceCond getResourceCond() {
         return resourceCond;
     }
@@ -326,7 +345,7 @@ public class SearchCond extends AbstractSearchCond {
         switch (type) {
             case LEAF:
             case NOT_LEAF:
-                isValid = (anyTypeCond != null || anyCond != null || attributeCond != null
+                isValid = (anyTypeCond != null || anyCond != null || attributeCond != null || dynRealmCond != null
                         || relationshipCond != null || relationshipTypeCond != null || membershipCond != null
                         || roleCond != null || resourceCond != null || assignableCond != null || memberCond != null)
                         && (anyTypeCond == null || anyTypeCond.isValid())

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java
new file mode 100644
index 0000000..e8be2aa
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+public interface DynRealm extends DynMembership<Any<?>>, ProvidedKeyEntity {
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
index 441f87a..68a1e9c 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
@@ -30,6 +30,10 @@ public interface Role extends ProvidedKeyEntity {
 
     List<? extends Realm> getRealms();
 
+    boolean add(DynRealm dynRealm);
+
+    List<? extends DynRealm> getDynRealms();
+
     DynRoleMembership getDynMembership();
 
     void setDynMembership(DynRoleMembership dynMembership);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
index bd95fe8..ce607bf 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
@@ -37,6 +37,7 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
@@ -143,10 +144,16 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
 
                         case ROLES:
                             RoleCond roleCond = new RoleCond();
-                            roleCond.setRoleKey(value);
+                            roleCond.setRole(value);
                             leaf = SearchCond.getLeafCond(roleCond);
                             break;
 
+                        case DYNREALMS:
+                            DynRealmCond dynRealmCond = new DynRealmCond();
+                            dynRealmCond.setDynRealm(value);
+                            leaf = SearchCond.getLeafCond(dynRealmCond);
+                            break;
+
                         case ASSIGNABLE:
                             AssignableCond assignableCond = new AssignableCond();
                             assignableCond.setRealmFullPath(realm);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 144d42d..08b2f12 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
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.api.search;
 
 import static org.junit.Assert.assertEquals;
 
+import java.util.UUID;
 import org.apache.syncope.common.lib.search.AnyObjectFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.search.GroupFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.search.SpecialAttr;
@@ -32,6 +33,7 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
@@ -109,7 +111,8 @@ public class SearchCondConverterTest {
 
     @Test
     public void nilike() {
-        String fiqlExpression = new UserFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("ros*").query();
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("ros*").
+                query();
         assertEquals("username!~ros*", fiqlExpression);
 
         AttributeCond attrCond = new AnyCond(AttributeCond.Type.ILIKE);
@@ -193,13 +196,26 @@ public class SearchCondConverterTest {
         assertEquals(SpecialAttr.ROLES + "==User reviewer", fiqlExpression);
 
         RoleCond roleCond = new RoleCond();
-        roleCond.setRoleKey("User reviewer");
+        roleCond.setRole("User reviewer");
         SearchCond simpleCond = SearchCond.getLeafCond(roleCond);
 
         assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
     }
 
     @Test
+    public void dynRealms() {
+        String dynRealm = UUID.randomUUID().toString();
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().inDynRealms(dynRealm).query();
+        assertEquals(SpecialAttr.DYNREALMS + "==" + dynRealm, fiqlExpression);
+
+        DynRealmCond dynRealmCond = new DynRealmCond();
+        dynRealmCond.setDynRealm(dynRealm);
+        SearchCond simpleCond = SearchCond.getLeafCond(dynRealmCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
     public void resources() {
         String fiqlExpression = new UserFiqlSearchConditionBuilder().hasResources("resource-ldap").query();
         assertEquals(SpecialAttr.RESOURCES + "==resource-ldap", fiqlExpression);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/pom.xml b/core/persistence-jpa/pom.xml
index af58940..9b2a5e4 100644
--- a/core/persistence-jpa/pom.xml
+++ b/core/persistence-jpa/pom.xml
@@ -140,6 +140,24 @@ under the License.
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.seleniumhq.selenium</groupId>
+      <artifactId>selenium-java</artifactId>
+      <scope>test</scope>
+      <version>2.44.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.opera</groupId>
+      <artifactId>operadriver</artifactId>
+      <scope>test</scope>
+      <version>1.5</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.seleniumhq.selenium</groupId>
+          <artifactId>selenium-remote-driver</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
index 0c4fdcb..89d27e9 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import static org.apache.syncope.core.persistence.jpa.dao.AbstractDAO.LOG;
+
 import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -41,6 +43,7 @@ import org.apache.syncope.core.persistence.api.dao.AllowedSchemas;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
@@ -51,6 +54,7 @@ import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.Schema;
@@ -80,6 +84,8 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
 
     private AnySearchDAO searchDAO;
 
+    private DynRealmDAO dynRealmDAO;
+
     private AnyUtils anyUtils;
 
     private PlainSchemaDAO plainSchemaDAO() {
@@ -109,6 +115,15 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
         return searchDAO;
     }
 
+    protected DynRealmDAO dynRealmDAO() {
+        synchronized (this) {
+            if (dynRealmDAO == null) {
+                dynRealmDAO = ApplicationContextProvider.getApplicationContext().getBean(DynRealmDAO.class);
+            }
+        }
+        return dynRealmDAO;
+    }
+
     protected abstract AnyUtils init();
 
     protected AnyUtils anyUtils() {
@@ -539,4 +554,28 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
 
         delete(any);
     }
+
+    @Transactional(readOnly = true)
+    @Override
+    public List<String> findDynRealms(final String key) {
+        Query query = entityManager().createNativeQuery(
+                "SELECT dynRealm_id FROM " + JPADynRealmDAO.DYNMEMB_TABLE + " WHERE any_id=?");
+        query.setParameter(1, key);
+
+        List<String> result = new ArrayList<>();
+        for (Object resultKey : query.getResultList()) {
+            String actualKey = resultKey instanceof Object[]
+                    ? (String) ((Object[]) resultKey)[0]
+                    : ((String) resultKey);
+
+            DynRealm dynRealm = dynRealmDAO().find(actualKey);
+            if (dynRealm == null) {
+                LOG.error("Could not find dynRealm with id {}, even though returned by the native query", actualKey);
+            } else if (!result.contains(dynRealm)) {
+                result.add(actualKey);
+            }
+        }
+        return result;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
index d9b4e95..7c1a1b0 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
@@ -29,6 +29,8 @@ import javax.persistence.Entity;
 import javax.validation.ValidationException;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.SerializationUtils;
 import org.apache.commons.lang3.tuple.Pair;
@@ -38,6 +40,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
@@ -45,6 +48,7 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
@@ -70,6 +74,9 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
     protected RealmDAO realmDAO;
 
     @Autowired
+    protected DynRealmDAO dynRealmDAO;
+
+    @Autowired
     protected AnyObjectDAO anyObjectDAO;
 
     @Autowired
@@ -92,6 +99,21 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
         return search(SearchCond.getLeafCond(assignableCond), kind);
     }
 
+    protected SearchCond buildEffectiveCond(final SearchCond cond, final Set<String> dynRealmKeys) {
+        List<SearchCond> effectiveConds = CollectionUtils.collect(dynRealmKeys, new Transformer<String, SearchCond>() {
+
+            @Override
+            public SearchCond transform(final String input) {
+                DynRealmCond dynRealmCond = new DynRealmCond();
+                dynRealmCond.setDynRealm(input);
+                return SearchCond.getLeafCond(dynRealmCond);
+            }
+        }, new ArrayList<SearchCond>());
+        effectiveConds.add(cond);
+
+        return SearchCond.getAndCond(effectiveConds);
+    }
+
     protected abstract int doCount(Set<String> adminRealms, SearchCond cond, AnyTypeKind kind);
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 d010e4b..e0545d2 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
@@ -143,6 +143,9 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
                 return anyObject.getRealm().getFullPath().startsWith(realm);
             }
         });
+        if (!authorized) {
+            authorized = !CollectionUtils.intersection(findDynRealms(anyObject.getKey()), authRealms).isEmpty();
+        }
         if (authRealms == null || authRealms.isEmpty() || !authorized) {
             throw new DelegatedAdministrationException(AnyTypeKind.ANY_OBJECT, anyObject.getKey());
         }
@@ -213,6 +216,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
         publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged, AuthContextUtils.getDomain()));
 
         groupDAO().refreshDynMemberships(merged);
+        dynRealmDAO().refreshDynMemberships(merged);
 
         return merged;
     }
@@ -238,6 +242,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     @Override
     public void delete(final AnyObject anyObject) {
         groupDAO().removeDynMemberships(anyObject);
+        dynRealmDAO().removeDynMemberships(anyObject.getKey());
 
         for (ARelationship relationship : findARelationships(anyObject)) {
             relationship.getLeftEnd().getRelationships().remove(relationship);
@@ -259,16 +264,16 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
-    public List<Group> findDynGroups(final AnyObject anyObject) {
+    public List<Group> findDynGroups(final String key) {
         Query query = entityManager().createNativeQuery(
                 "SELECT group_id FROM " + JPAGroupDAO.ADYNMEMB_TABLE + " WHERE any_id=?");
-        query.setParameter(1, anyObject.getKey());
+        query.setParameter(1, key);
 
         List<Group> result = new ArrayList<>();
-        for (Object key : query.getResultList()) {
-            String actualKey = key instanceof Object[]
-                    ? (String) ((Object[]) key)[0]
-                    : ((String) key);
+        for (Object resultKey : query.getResultList()) {
+            String actualKey = resultKey instanceof Object[]
+                    ? (String) ((Object[]) resultKey)[0]
+                    : ((String) resultKey);
 
             Group group = groupDAO().find(actualKey);
             if (group == null) {
@@ -291,7 +296,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
                         return input.getRightEnd();
                     }
                 }, new ArrayList<Group>()),
-                findDynGroups(anyObject));
+                findDynGroups(anyObject.getKey()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index 61d4a7f..cc44ef8 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -44,11 +44,13 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.Realm;
@@ -61,21 +63,34 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
 
     private static final String EMPTY_QUERY = "SELECT any_id FROM user_search_attr WHERE 1=2";
 
-    private String getAdminRealmsFilter(
+    private Pair<String, Set<String>> getAdminRealmsFilter(
             final Set<String> adminRealms,
             final SearchSupport svs,
             final List<Object> parameters) {
 
         Set<String> realmKeys = new HashSet<>();
+        Set<String> dynRealmKeys = new HashSet<>();
         for (String realmPath : RealmUtils.normalize(adminRealms)) {
-            Realm realm = realmDAO.findByFullPath(realmPath);
-            if (realm == null) {
-                LOG.warn("Ignoring invalid realm {}", realmPath);
+            if (realmPath.startsWith("/")) {
+                Realm realm = realmDAO.findByFullPath(realmPath);
+                if (realm == null) {
+                    LOG.warn("Ignoring invalid realm {}", realmPath);
+                } else {
+                    CollectionUtils.collect(
+                            realmDAO.findDescendants(realm), EntityUtils.<Realm>keyTransformer(), realmKeys);
+                }
             } else {
-                CollectionUtils.collect(
-                        realmDAO.findDescendants(realm), EntityUtils.<Realm>keyTransformer(), realmKeys);
+                DynRealm dynRealm = dynRealmDAO.find(realmPath);
+                if (dynRealm == null) {
+                    LOG.warn("Ignoring invalid dynamic realm {}", realmPath);
+                } else {
+                    dynRealmKeys.add(dynRealm.getKey());
+                }
             }
         }
+        if (!dynRealmKeys.isEmpty()) {
+            CollectionUtils.collect(realmDAO.findAll(), EntityUtils.keyTransformer(), realmKeys);
+        }
 
         StringBuilder adminRealmFilter = new StringBuilder("u.any_id IN (").
                 append("SELECT any_id FROM ").append(svs.field().name).
@@ -94,20 +109,23 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
 
         adminRealmFilter.append("))");
 
-        return adminRealmFilter.toString();
+        return Pair.of(adminRealmFilter.toString(), dynRealmKeys);
     }
 
     @Override
     protected int doCount(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) {
         List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
 
-        // 1. get the query string from the search condition
         SearchSupport svs = new SearchSupport(kind);
-        StringBuilder queryString = getQuery(cond, parameters, svs);
+
+        Pair<String, Set<String>> filter = getAdminRealmsFilter(adminRealms, svs, parameters);
+
+        // 1. get the query string from the search condition
+        StringBuilder queryString = getQuery(buildEffectiveCond(cond, filter.getRight()), parameters, svs);
 
         // 2. take into account administrative realms
         queryString.insert(0, "SELECT u.any_id FROM (");
-        queryString.append(") u WHERE ").append(getAdminRealmsFilter(adminRealms, svs, parameters));
+        queryString.append(") u WHERE ").append(filter.getLeft());
 
         // 3. prepare the COUNT query
         queryString.insert(0, "SELECT COUNT(any_id) FROM (");
@@ -132,9 +150,12 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
         try {
             List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
 
-            // 1. get the query string from the search condition
             SearchSupport svs = new SearchSupport(kind);
-            StringBuilder queryString = getQuery(cond, parameters, svs);
+
+            Pair<String, Set<String>> filter = getAdminRealmsFilter(adminRealms, svs, parameters);
+
+            // 1. get the query string from the search condition
+            StringBuilder queryString = getQuery(buildEffectiveCond(cond, filter.getRight()), parameters, svs);
 
             // 2. take into account realms and ordering
             OrderBySupport obs = parseOrderBy(kind, svs, orderBy);
@@ -146,7 +167,7 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
                 queryString.append(')').append(buildWhere(svs, obs));
             }
             queryString.
-                    append(getAdminRealmsFilter(adminRealms, svs, parameters)).
+                    append(filter.getLeft()).
                     append(buildOrderBy(obs));
 
             // 3. prepare the search query
@@ -340,6 +361,9 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
                 } else if (cond.getRoleCond() != null && AnyTypeKind.USER == svs.anyTypeKind) {
                     query.append(getQuery(cond.getRoleCond(),
                             cond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
+                } else if (cond.getDynRealmCond() != null) {
+                    query.append(getQuery(cond.getDynRealmCond(),
+                            cond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
                 } else if (cond.getMemberCond() != null && AnyTypeKind.GROUP == svs.anyTypeKind) {
                     query.append(getQuery(cond.getMemberCond(),
                             cond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
@@ -503,7 +527,7 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
 
         query.append("SELECT DISTINCT any_id FROM ").
                 append(svs.role().name).append(" WHERE ").
-                append("role_id=?").append(setParameter(parameters, cond.getRoleKey())).
+                append("role_id=?").append(setParameter(parameters, cond.getRole())).
                 append(") ");
 
         if (not) {
@@ -514,7 +538,27 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
 
         query.append("SELECT DISTINCT any_id FROM ").
                 append(svs.dynrolemembership().name).append(" WHERE ").
-                append("role_id=?").append(setParameter(parameters, cond.getRoleKey())).
+                append("role_id=?").append(setParameter(parameters, cond.getRole())).
+                append("))");
+
+        return query.toString();
+    }
+
+    private String getQuery(
+            final DynRealmCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
+
+        StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+                append(svs.field().name).append(" WHERE (");
+
+        if (not) {
+            query.append("any_id NOT IN (");
+        } else {
+            query.append("any_id IN (");
+        }
+
+        query.append("SELECT DISTINCT any_id FROM ").
+                append(svs.dynrealmmembership().name).append(" WHERE ").
+                append("dynRealm_id=?").append(setParameter(parameters, cond.getDynRealm())).
                 append("))");
 
         return query.toString();

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java
new file mode 100644
index 0000000..874f0d3
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.util.List;
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.persistence.jpa.entity.JPADynRealm;
+import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+@Repository
+public class JPADynRealmDAO extends AbstractDAO<DynRealm> implements DynRealmDAO {
+
+    public static final String DYNMEMB_TABLE = "DynRealmMembers";
+
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
+    private AnySearchDAO searchDAO;
+
+    private AnySearchDAO searchDAO() {
+        synchronized (this) {
+            if (searchDAO == null) {
+                searchDAO = ApplicationContextProvider.getApplicationContext().getBean(AnySearchDAO.class);
+            }
+        }
+        return searchDAO;
+    }
+
+    @Override
+    public DynRealm find(final String key) {
+        return entityManager().find(JPADynRealm.class, key);
+    }
+
+    @Override
+    public List<DynRealm> findAll() {
+        TypedQuery<DynRealm> query = entityManager().createQuery(
+                "SELECT e FROM " + JPADynRealm.class.getSimpleName() + " e ", DynRealm.class);
+        return query.getResultList();
+    }
+
+    @Override
+    public DynRealm save(final DynRealm dynRealm) {
+        DynRealm merged = entityManager().merge(dynRealm);
+
+        // refresh dynamic memberships
+        if (merged.getFIQLCond() != null) {
+            clearDynMembers(merged);
+
+            List<Any<?>> matching = searchDAO().search(
+                    SearchCondConverter.convert(merged.getFIQLCond()), AnyTypeKind.USER);
+            for (Any<?> any : matching) {
+                Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
+                insert.setParameter(1, any.getKey());
+                insert.setParameter(2, merged.getKey());
+                insert.executeUpdate();
+
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, any, AuthContextUtils.getDomain()));
+            }
+
+            matching = searchDAO().search(
+                    SearchCondConverter.convert(merged.getFIQLCond()), AnyTypeKind.GROUP);
+            for (Any<?> any : matching) {
+                Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
+                insert.setParameter(1, any.getKey());
+                insert.setParameter(2, merged.getKey());
+                insert.executeUpdate();
+
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, any, AuthContextUtils.getDomain()));
+            }
+
+            matching = searchDAO().search(
+                    SearchCondConverter.convert(merged.getFIQLCond()), AnyTypeKind.ANY_OBJECT);
+            for (Any<?> any : matching) {
+                Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
+                insert.setParameter(1, any.getKey());
+                insert.setParameter(2, merged.getKey());
+                insert.executeUpdate();
+
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, any, AuthContextUtils.getDomain()));
+            }
+        }
+
+        return merged;
+    }
+
+    @Override
+    public void delete(final String key) {
+        DynRealm dynRealm = find(key);
+        if (dynRealm == null) {
+            return;
+        }
+
+        clearDynMembers(dynRealm);
+
+        entityManager().remove(dynRealm);
+    }
+
+    @Override
+    public void clearDynMembers(final DynRealm dynRealm) {
+        Query delete = entityManager().createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE dynRealm_id=?");
+        delete.setParameter(1, dynRealm.getKey());
+        delete.executeUpdate();
+    }
+
+    @Transactional
+    @Override
+    public void refreshDynMemberships(final Any<?> any) {
+        for (DynRealm dynRealm : findAll()) {
+            if (dynRealm.getFIQLCond() != null) {
+                Query delete = entityManager().createNativeQuery(
+                        "DELETE FROM " + DYNMEMB_TABLE + " WHERE dynRealm_id=? AND any_id=?");
+                delete.setParameter(1, dynRealm.getKey());
+                delete.setParameter(2, any.getKey());
+                delete.executeUpdate();
+
+                if (searchDAO().matches(any, SearchCondConverter.convert(dynRealm.getFIQLCond()))) {
+                    Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
+                    insert.setParameter(1, any.getKey());
+                    insert.setParameter(2, dynRealm.getKey());
+                    insert.executeUpdate();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void removeDynMemberships(final String key) {
+        Query delete = entityManager().createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE any_id=?");
+        delete.setParameter(1, key);
+        delete.executeUpdate();
+    }
+
+}


[2/4] syncope git commit: [SYNCOPE-1067] Core modifications

Posted by il...@apache.org.
[SYNCOPE-1067] Core modifications


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

Branch: refs/heads/2_0_X
Commit: 062c87a25b78715edbbac66014b7abc342e4fd83
Parents: b61b092
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Jun 8 17:54:45 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Jun 8 17:54:45 2017 +0200

----------------------------------------------------------------------
 .../console/panels/RoleDirectoryPanel.java      |   2 +
 .../panels/RoleDirectoryPanel.properties        |   1 +
 .../panels/RoleDirectoryPanel_it.properties     |   1 +
 .../panels/RoleDirectoryPanel_pt_BR.properties  |   1 +
 .../panels/RoleDirectoryPanel_ru.properties     |   1 +
 .../AbstractFiqlSearchConditionBuilder.java     |  25 +++
 .../syncope/common/lib/search/SpecialAttr.java  |   4 +
 .../common/lib/search/SyncopeProperty.java      |   4 +
 .../search/UserFiqlSearchConditionBuilder.java  |  12 --
 .../org/apache/syncope/common/lib/to/AnyTO.java |   9 +
 .../syncope/common/lib/to/DynRealmTO.java       |  55 +++++
 .../apache/syncope/common/lib/to/RoleTO.java    |   9 +
 .../common/lib/types/ClientExceptionType.java   |   1 +
 .../common/lib/types/EntityViolationType.java   |   1 +
 .../common/lib/types/StandardEntitlement.java   |  10 +
 .../rest/api/service/DynRealmService.java       |  90 ++++++++
 .../syncope/core/logic/AbstractAnyLogic.java    |  42 +++-
 .../syncope/core/logic/AnyObjectLogic.java      |   4 -
 .../syncope/core/logic/DynRealmLogic.java       | 127 +++++++++++
 .../apache/syncope/core/logic/GroupLogic.java   |   8 -
 .../apache/syncope/core/logic/UserLogic.java    |   8 -
 .../core/persistence/api/dao/AnyDAO.java        |   1 +
 .../core/persistence/api/dao/AnyObjectDAO.java  |   2 +-
 .../core/persistence/api/dao/DynRealmDAO.java   |  41 ++++
 .../core/persistence/api/dao/RoleDAO.java       |   2 +-
 .../core/persistence/api/dao/UserDAO.java       |   4 +-
 .../api/dao/search/DynRealmCond.java            |  39 ++++
 .../persistence/api/dao/search/RoleCond.java    |  12 +-
 .../persistence/api/dao/search/SearchCond.java  |  25 ++-
 .../core/persistence/api/entity/DynRealm.java   |  23 ++
 .../core/persistence/api/entity/Role.java       |   4 +
 .../api/search/SearchCondVisitor.java           |   9 +-
 .../api/search/SearchCondConverterTest.java     |  20 +-
 core/persistence-jpa/pom.xml                    |  18 ++
 .../persistence/jpa/dao/AbstractAnyDAO.java     |  39 ++++
 .../jpa/dao/AbstractAnySearchDAO.java           |  22 ++
 .../persistence/jpa/dao/JPAAnyObjectDAO.java    |  19 +-
 .../persistence/jpa/dao/JPAAnySearchDAO.java    |  74 +++++--
 .../persistence/jpa/dao/JPADynRealmDAO.java     | 162 ++++++++++++++
 .../core/persistence/jpa/dao/JPAGroupDAO.java   |  16 +-
 .../core/persistence/jpa/dao/JPARoleDAO.java    |   6 +-
 .../core/persistence/jpa/dao/JPAUserDAO.java    |  37 ++--
 .../core/persistence/jpa/dao/SearchSupport.java |   4 +
 .../persistence/jpa/entity/JPADynRealm.java     |  50 +++++
 .../jpa/entity/JPAEntityFactory.java            |   3 +
 .../core/persistence/jpa/entity/JPARole.java    |  20 ++
 .../jpa/validation/entity/DynRealmCheck.java    |  41 ++++
 .../validation/entity/DynRealmValidator.java    |  47 +++++
 .../entity/EntityValidationListener.java        |   2 +
 .../src/main/resources/indexes.xml              |   3 +
 .../src/main/resources/views.xml                |   6 +
 .../persistence/jpa/inner/AnySearchTest.java    |   4 +-
 .../core/persistence/jpa/inner/RoleTest.java    |   1 -
 .../persistence/jpa/outer/AnySearchTest.java    |   2 +-
 .../persistence/jpa/outer/DynRealmTest.java     |  84 ++++++++
 .../api/data/DynRealmDataBinder.java            |  31 +++
 .../java/data/AnyObjectDataBinderImpl.java      |   5 +-
 .../java/data/DynRealmDataBinderImpl.java       |  72 +++++++
 .../java/data/GroupDataBinderImpl.java          |   7 +-
 .../java/data/RoleDataBinderImpl.java           |  18 ++
 .../java/data/UserDataBinderImpl.java           |   7 +-
 .../rest/cxf/service/DynRealmServiceImpl.java   |  66 ++++++
 .../core/spring/security/AuthDataAccessor.java  |  69 +++---
 .../client/ElasticsearchUtils.java              |   8 +-
 .../jpa/dao/ElasticsearchAnySearchDAO.java      |  49 ++++-
 .../org/apache/syncope/fit/AbstractITCase.java  |   4 +
 .../apache/syncope/fit/core/DynRealmITCase.java | 211 +++++++++++++++++++
 .../syncope/fit/core/ExceptionMapperITCase.java |   2 +-
 68 files changed, 1652 insertions(+), 154 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
index d911887..1251b6d 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/RoleDirectoryPanel.java
@@ -115,6 +115,8 @@ public class RoleDirectoryPanel extends DirectoryPanel<RoleTO, RoleWrapper, Role
                 new ResourceModel("entitlements", "Entitlements"), null, "entitlements"));
         columns.add(new PropertyColumn<RoleTO, String>(
                 new ResourceModel("realms"), null, "realms"));
+        columns.add(new PropertyColumn<RoleTO, String>(
+                new ResourceModel("dynRealms"), null, "dynRealms"));
 
         return columns;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel.properties
index 3a27730..d656ca5 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel.properties
@@ -17,3 +17,4 @@
 any.edit=Edit role ${roleTO.key}
 any.new=New role
 role.members=Role '${key}' members
+dynRealms=Dynamic Realms

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_it.properties
index 04a466e..34ba4ef 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_it.properties
@@ -17,3 +17,4 @@
 any.edit=Modifica ruolo ${roleTO.key}
 any.new=Nuovo ruolo
 role.members=Utenti membri del rulo '${key}'
+dynRealms=Realm dinamici

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_pt_BR.properties
index 1de6e43..4e55e7e 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_pt_BR.properties
@@ -17,3 +17,4 @@
 any.edit=Alterar fun\u00e7\u00e3o ${roleTO.key}
 any.new=Novo fun\u00e7\u00e3o
 role.members=Role '${key}' members
+dynRealms=Dynamic Realms

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_ru.properties
index b292ea7..61dd821 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_ru.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/RoleDirectoryPanel_ru.properties
@@ -18,3 +18,4 @@
 any.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0440\u043e\u043b\u044c ${roleTO.key}
 any.new=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0440\u043e\u043b\u044c
 role.members=\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u0440\u043e\u043b\u0438 '${key}'
+dynRealms=Dynamic Realms

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
index 3338c1b..18fee59 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
@@ -65,6 +65,18 @@ public abstract class AbstractFiqlSearchConditionBuilder extends FiqlSearchCondi
         return newBuilderInstance().is(property).notNullValue();
     }
 
+    public CompleteCondition inDynRealms(final String dynRealm, final String... moreDynRealms) {
+        return newBuilderInstance().
+                is(SpecialAttr.DYNREALMS.toString()).
+                inDynRealms(dynRealm, moreDynRealms);
+    }
+
+    public CompleteCondition notInDynRealms(final String dynRealm, final String... moreDynRealms) {
+        return newBuilderInstance().
+                is(SpecialAttr.DYNREALMS.toString()).
+                notInDynRealms(dynRealm, moreDynRealms);
+    }
+
     public CompleteCondition hasResources(final String resource, final String... moreResources) {
         return newBuilderInstance().is(SpecialAttr.RESOURCES.toString()).hasResources(resource, moreResources);
     }
@@ -122,5 +134,18 @@ public abstract class AbstractFiqlSearchConditionBuilder extends FiqlSearchCondi
         public CompleteCondition notEqualTolIgnoreCase(final String literalOrPattern) {
             return condition(SyncopeFiqlParser.NIEQ, literalOrPattern);
         }
+
+        @Override
+        public CompleteCondition inDynRealms(final String dynRealm, final String... moreDynRealms) {
+            this.result = SpecialAttr.DYNREALMS.toString();
+            return condition(FiqlParser.EQ, dynRealm, (Object[]) moreDynRealms);
+        }
+
+        @Override
+        public CompleteCondition notInDynRealms(final String dynRealm, final String... moreDynRealms) {
+            this.result = SpecialAttr.DYNREALMS.toString();
+            return condition(FiqlParser.NEQ, dynRealm, (Object[]) moreDynRealms);
+        }
+
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
index d8b6672..c744169 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
@@ -56,6 +56,10 @@ public enum SpecialAttr {
      */
     ROLES("$roles"),
     /**
+     * Applies to users, groups and any objects.
+     */
+    DYNREALMS("$dynRealms"),
+    /**
      * Applies to groups.
      */
     MEMBER("$member"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
index 0d06c5b..fe2d47e 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
@@ -70,4 +70,8 @@ public interface SyncopeProperty extends Property {
      */
     CompleteCondition hasNotResources(String resource, String... moreResources);
 
+    CompleteCondition inDynRealms(String dynRealm, String... moreDynRealms);
+
+    CompleteCondition notInDynRealms(String dynRealm, String... moreDynRealms);
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
index 89d81c7..8cd7421 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
@@ -88,18 +88,6 @@ public class UserFiqlSearchConditionBuilder extends AbstractFiqlSearchConditionB
                 notInRoles(role, moreRoles);
     }
 
-    @Override
-    public CompleteCondition hasResources(final String resource, final String... moreResources) {
-        return newBuilderInstance().
-                is(SpecialAttr.RESOURCES.toString()).
-                hasResources(resource, moreResources);
-    }
-
-    @Override
-    public CompleteCondition hasNotResources(final String resource, final String... moreResources) {
-        return newBuilderInstance().is(SpecialAttr.RESOURCES.toString()).hasNotResources(resource, moreResources);
-    }
-
     protected static class Builder extends AbstractFiqlSearchConditionBuilder.Builder
             implements UserProperty, CompleteCondition {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
index b63d4b0..47f1d9b 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
@@ -46,6 +46,8 @@ public abstract class AnyTO extends AbstractAnnotatedBean implements EntityTO, A
 
     private String realm;
 
+    private List<String> dynRealms = new ArrayList<>();
+
     private String status;
 
     private final List<String> auxClasses = new ArrayList<>();
@@ -84,6 +86,13 @@ public abstract class AnyTO extends AbstractAnnotatedBean implements EntityTO, A
         this.realm = realm;
     }
 
+    @XmlElementWrapper(name = "dynRealms")
+    @XmlElement(name = "dynRealmF")
+    @JsonProperty("dynRealms")
+    public List<String> getDynRealms() {
+        return dynRealms;
+    }
+
     public String getStatus() {
         return status;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java
new file mode 100644
index 0000000..c218ec6
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/DynRealmTO.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.to;
+
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "dynRealm")
+@XmlType
+public class DynRealmTO extends AbstractBaseBean implements EntityTO {
+
+    private static final long serialVersionUID = 4560822655754800031L;
+
+    private String key;
+
+    private String cond;
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @PathParam("key")
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    public String getCond() {
+        return cond;
+    }
+
+    public void setCond(final String cond) {
+        this.cond = cond;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
index cbb8784..9313349 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
@@ -42,6 +42,8 @@ public class RoleTO extends AbstractBaseBean implements EntityTO {
 
     private final List<String> realms = new ArrayList<>();
 
+    private final List<String> dynRealms = new ArrayList<>();
+
     private String dynMembershipCond;
 
     @Override
@@ -69,6 +71,13 @@ public class RoleTO extends AbstractBaseBean implements EntityTO {
         return realms;
     }
 
+    @XmlElementWrapper(name = "dynRealms")
+    @XmlElement(name = "dynRealm")
+    @JsonProperty("dynRealms")
+    public List<String> getDynRealms() {
+        return dynRealms;
+    }
+
     public String getDynMembershipCond() {
         return dynMembershipCond;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
index 9954b9b..1927737 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
@@ -58,6 +58,7 @@ public enum ClientExceptionType {
     InvalidMapping(Response.Status.BAD_REQUEST),
     InvalidMembership(Response.Status.BAD_REQUEST),
     InvalidRealm(Response.Status.BAD_REQUEST),
+    InvalidDynRealm(Response.Status.BAD_REQUEST),
     InvalidRole(Response.Status.BAD_REQUEST),
     InvalidUser(Response.Status.BAD_REQUEST),
     InvalidExternalResource(Response.Status.BAD_REQUEST),

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
index 3107536..6cc7e7c 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
@@ -35,6 +35,7 @@ public enum EntityViolationType {
     InvalidPolicy("org.apache.syncope.core.persistence.validation.policy"),
     InvalidPropagationTask("org.apache.syncope.core.persistence.validation.propagationtask"),
     InvalidRealm("org.apache.syncope.core.persistence.validation.realm"),
+    InvalidDynRealm("org.apache.syncope.core.persistence.validation.dynrealm"),
     InvalidReport("org.apache.syncope.core.persistence.validation.report"),
     InvalidResource("org.apache.syncope.core.persistence.validation.externalresource"),
     InvalidGroupOwner("org.apache.syncope.core.persistence.validation.group.owner"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
index aa3fd17..81709f6 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
@@ -78,6 +78,16 @@ public final class StandardEntitlement {
 
     public static final String ROLE_DELETE = "ROLE_DELETE";
 
+    public static final String DYNREALM_LIST = "DYNREALM_LIST";
+
+    public static final String DYNREALM_CREATE = "DYNREALM_CREATE";
+
+    public static final String DYNREALM_READ = "DYNREALM_READ";
+
+    public static final String DYNREALM_UPDATE = "DYNREALM_UPDATE";
+
+    public static final String DYNREALM_DELETE = "DYNREALM_DELETE";
+
     public static final String SCHEMA_LIST = "SCHEMA_LIST";
 
     public static final String SCHEMA_CREATE = "SCHEMA_CREATE";

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/DynRealmService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/DynRealmService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/DynRealmService.java
new file mode 100644
index 0000000..dc9836d
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/DynRealmService.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.DynRealmTO;
+
+/**
+ * REST operations for dynamic realms.
+ */
+@Path("dynRealms")
+public interface DynRealmService extends JAXRSService {
+
+    /**
+     * Returns a list of all dynamic realms.
+     *
+     * @return list of all dynamic realms.
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    List<DynRealmTO> list();
+
+    /**
+     * Returns dynamic realm with matching key.
+     *
+     * @param key dynamic realm key to be read
+     * @return dynamic realm with matching key
+     */
+    @GET
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    DynRealmTO read(@NotNull @PathParam("key") String key);
+
+    /**
+     * Creates a new dynamic realm.
+     *
+     * @param dynDynRealmTO dynamic realm to be created
+     * @return Response object featuring Location header of created dynamic realm
+     */
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    Response create(@NotNull DynRealmTO dynDynRealmTO);
+
+    /**
+     * Updates the dynamic realm matching the provided key.
+     *
+     * @param dynDynRealmTO dynamic realm to be stored
+     */
+    @PUT
+    @Path("{key}")
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    void update(@NotNull DynRealmTO dynDynRealmTO);
+
+    /**
+     * Deletes the dynamic realm matching the provided key.
+     *
+     * @param key dynamic realm key to be deleted
+     */
+    @DELETE
+    @Path("{key}")
+    void delete(@NotNull @PathParam("key") String key);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/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 003058f..066b3d2 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
@@ -38,12 +38,16 @@ import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
 import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
 import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
@@ -55,6 +59,15 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
 public abstract class AbstractAnyLogic<TO extends AnyTO, P extends AnyPatch> extends AbstractResourceAssociator<TO> {
 
     @Autowired
+    protected UserDAO userDAO;
+
+    @Autowired
+    protected GroupDAO groupDAO;
+
+    @Autowired
+    protected AnyObjectDAO anyObjectDAO;
+
+    @Autowired
     private RealmDAO realmDAO;
 
     @Autowired
@@ -191,9 +204,7 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P extends AnyPatch> ext
 
     }
 
-    protected Set<String> getEffectiveRealms(
-            final Set<String> allowedRealms, final String requestedRealm) {
-
+    protected Set<String> getEffectiveRealms(final Set<String> allowedRealms, final String requestedRealm) {
         Set<String> allowed = RealmUtils.normalize(allowedRealms);
         Set<String> requested = new HashSet<>();
         requested.add(requestedRealm);
@@ -202,18 +213,35 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P extends AnyPatch> ext
         CollectionUtils.select(requested, new StartsWithPredicate(allowed), effective);
         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);
+
         return effective;
     }
 
     protected void securityChecks(final Set<String> effectiveRealms, final String realm, final String key) {
-        if (!IterableUtils.matchesAny(effectiveRealms, new Predicate<String>() {
+        boolean authorized = IterableUtils.matchesAny(effectiveRealms, new Predicate<String>() {
 
             @Override
             public boolean evaluate(final String ownedRealm) {
                 return realm.startsWith(ownedRealm);
             }
-        })) {
-
+        });
+        if (!authorized) {
+            AnyDAO<?> anyDAO = this instanceof UserLogic
+                    ? userDAO
+                    : this instanceof GroupLogic
+                            ? groupDAO
+                            : anyObjectDAO;
+            authorized = !CollectionUtils.intersection(anyDAO.findDynRealms(key), effectiveRealms).isEmpty();
+        }
+        if (!authorized) {
             throw new DelegatedAdministrationException(
                     this instanceof UserLogic
                             ? AnyTypeKind.USER
@@ -225,7 +253,7 @@ public abstract class AbstractAnyLogic<TO extends AnyTO, P extends AnyPatch> ext
     }
 
     public abstract Date findLastChange(String key);
-    
+
     public abstract TO read(String key);
 
     public abstract int count(String realm);

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/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 b4779df..c585dd0 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
@@ -39,7 +39,6 @@ import org.apache.syncope.common.lib.types.AnyEntitlement;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.PatchOperation;
-import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 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.dao.search.OrderByClause;
@@ -62,9 +61,6 @@ import org.springframework.transaction.annotation.Transactional;
 public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectPatch> {
 
     @Autowired
-    protected AnyObjectDAO anyObjectDAO;
-
-    @Autowired
     protected AnySearchDAO searchDAO;
 
     @Autowired

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/logic/src/main/java/org/apache/syncope/core/logic/DynRealmLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/DynRealmLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/DynRealmLogic.java
new file mode 100644
index 0000000..bed7028
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/DynRealmLogic.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.to.DynRealmTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.provisioning.api.data.DynRealmDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DynRealmLogic extends AbstractTransactionalLogic<DynRealmTO> {
+
+    @Autowired
+    private DynRealmDataBinder binder;
+
+    @Autowired
+    private DynRealmDAO dynRealmDAO;
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.DYNREALM_READ + "')")
+    public DynRealmTO read(final String key) {
+        DynRealm dynRealm = dynRealmDAO.find(key);
+        if (dynRealm == null) {
+            LOG.error("Could not find dynamic realm '" + key + "'");
+
+            throw new NotFoundException(key);
+        }
+
+        return binder.getDynRealmTO(dynRealm);
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.DYNREALM_LIST + "')")
+    public List<DynRealmTO> list() {
+        return CollectionUtils.collect(dynRealmDAO.findAll(), new Transformer<DynRealm, DynRealmTO>() {
+
+            @Override
+            public DynRealmTO transform(final DynRealm input) {
+                return binder.getDynRealmTO(input);
+            }
+        }, new ArrayList<DynRealmTO>());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.DYNREALM_CREATE + "')")
+    public DynRealmTO create(final DynRealmTO dynRealmTO) {
+        return binder.getDynRealmTO(dynRealmDAO.save(binder.create(dynRealmTO)));
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.DYNREALM_UPDATE + "')")
+    public DynRealmTO update(final DynRealmTO dynRealmTO) {
+        DynRealm dynRealm = dynRealmDAO.find(dynRealmTO.getKey());
+        if (dynRealm == null) {
+            LOG.error("Could not find dynamic realm '" + dynRealmTO.getKey() + "'");
+            throw new NotFoundException(dynRealmTO.getKey());
+        }
+
+        return binder.getDynRealmTO(dynRealmDAO.save(binder.update(dynRealm, dynRealmTO)));
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.DYNREALM_DELETE + "')")
+    public DynRealmTO delete(final String key) {
+        DynRealm dynRealm = dynRealmDAO.find(key);
+        if (dynRealm == null) {
+            LOG.error("Could not find dynamic realm '" + key + "'");
+
+            throw new NotFoundException(key);
+        }
+
+        DynRealmTO deleted = binder.getDynRealmTO(dynRealm);
+        dynRealmDAO.delete(key);
+        return deleted;
+    }
+
+    @Override
+    protected DynRealmTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        String key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof String) {
+                    key = (String) args[i];
+                } else if (args[i] instanceof DynRealmTO) {
+                    key = ((DynRealmTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if (key != null) {
+            try {
+                return binder.getDynRealmTO(dynRealmDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/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 b93142d..1ddc108 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
@@ -49,10 +49,8 @@ import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
@@ -84,12 +82,6 @@ import org.springframework.transaction.annotation.Transactional;
 public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupPatch> {
 
     @Autowired
-    protected GroupDAO groupDAO;
-
-    @Autowired
-    protected UserDAO userDAO;
-
-    @Autowired
     protected AnySearchDAO searchDAO;
 
     @Autowired

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/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 7cc5414..02d88cb 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
@@ -45,9 +45,7 @@ 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.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -70,12 +68,6 @@ import org.springframework.transaction.annotation.Transactional;
 public class UserLogic extends AbstractAnyLogic<UserTO, UserPatch> {
 
     @Autowired
-    protected UserDAO userDAO;
-
-    @Autowired
-    protected GroupDAO groupDAO;
-
-    @Autowired
     protected AnySearchDAO searchDAO;
 
     @Autowired

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
index 979e39b..75b0411 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
@@ -93,4 +93,5 @@ public interface AnyDAO<A extends Any<?>> extends DAO<A> {
 
     void delete(A any);
 
+    List<String> findDynRealms(String key);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
index 448aca6..8a10f0b 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
@@ -43,7 +43,7 @@ public interface AnyObjectDAO extends AnyDAO<AnyObject> {
 
     AnyObject authFindByName(String name);
 
-    List<Group> findDynGroups(AnyObject anyObject);
+    List<Group> findDynGroups(String key);
 
     List<ARelationship> findAllRelationships(AnyObject anyObject);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DynRealmDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DynRealmDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DynRealmDAO.java
new file mode 100644
index 0000000..a782ea8
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DynRealmDAO.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+
+public interface DynRealmDAO extends DAO<DynRealm> {
+
+    DynRealm find(String key);
+
+    List<DynRealm> findAll();
+
+    DynRealm save(DynRealm dynRealm);
+
+    void delete(String key);
+
+    void clearDynMembers(DynRealm dynRealm);
+
+    void refreshDynMemberships(Any<?> any);
+
+    void removeDynMemberships(String key);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java
index f76b382..cc29852 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java
@@ -45,6 +45,6 @@ public interface RoleDAO extends DAO<Role> {
 
     void refreshDynMemberships(User user);
 
-    void removeDynMemberships(User user);
+    void removeDynMemberships(String key);
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
index 9e44bb3..2a5d76d 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
@@ -42,11 +42,11 @@ public interface UserDAO extends AnyDAO<User> {
 
     List<User> findBySecurityQuestion(SecurityQuestion securityQuestion);
 
-    List<Role> findDynRoles(User user);
+    List<Role> findDynRoles(String key);
 
     Collection<Role> findAllRoles(User user);
 
-    List<Group> findDynGroups(User user);
+    List<Group> findDynGroups(String key);
 
     Collection<Group> findAllGroups(User user);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/DynRealmCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/DynRealmCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/DynRealmCond.java
new file mode 100644
index 0000000..e3f3590
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/DynRealmCond.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao.search;
+
+public class DynRealmCond extends AbstractSearchCond {
+
+    private static final long serialVersionUID = 9110213614796095482L;
+
+    private String dynRealm;
+
+    public String getDynRealm() {
+        return dynRealm;
+    }
+
+    public void setDynRealm(final String dynRealm) {
+        this.dynRealm = dynRealm;
+    }
+
+    @Override
+    public final boolean isValid() {
+        return dynRealm != null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RoleCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RoleCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RoleCond.java
index 0ee7648..08972fa 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RoleCond.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/RoleCond.java
@@ -22,18 +22,18 @@ public class RoleCond extends AbstractSearchCond {
 
     private static final long serialVersionUID = 3581958527829522490L;
 
-    private String roleKey;
+    private String role;
 
-    public String getRoleKey() {
-        return roleKey;
+    public String getRole() {
+        return role;
     }
 
-    public void setRoleKey(final String roleKey) {
-        this.roleKey = roleKey;
+    public void setRole(final String role) {
+        this.role = role;
     }
 
     @Override
     public final boolean isValid() {
-        return roleKey != null;
+        return role != null;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
index e87df92..520fc58 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
@@ -49,6 +49,8 @@ public class SearchCond extends AbstractSearchCond {
 
     private RoleCond roleCond;
 
+    private DynRealmCond dynRealmCond;
+
     private ResourceCond resourceCond;
 
     private AssignableCond assignableCond;
@@ -117,6 +119,15 @@ public class SearchCond extends AbstractSearchCond {
         return nodeCond;
     }
 
+    public static SearchCond getLeafCond(final DynRealmCond dynRealmCond) {
+        SearchCond nodeCond = new SearchCond();
+
+        nodeCond.type = Type.LEAF;
+        nodeCond.dynRealmCond = dynRealmCond;
+
+        return nodeCond;
+    }
+
     public static SearchCond getLeafCond(final ResourceCond resourceCond) {
         SearchCond nodeCond = new SearchCond();
 
@@ -196,7 +207,9 @@ public class SearchCond extends AbstractSearchCond {
     }
 
     public static SearchCond getAndCond(final List<SearchCond> conditions) {
-        if (conditions.size() > 2) {
+        if (conditions.size() == 1) {
+            return conditions.get(0);
+        } else if (conditions.size() > 2) {
             SearchCond removed = conditions.remove(0);
             return getAndCond(removed, getAndCond(conditions));
         } else {
@@ -215,7 +228,9 @@ public class SearchCond extends AbstractSearchCond {
     }
 
     public static SearchCond getOrCond(final List<SearchCond> conditions) {
-        if (conditions.size() > 2) {
+        if (conditions.size() == 1) {
+            return conditions.get(0);
+        } else if (conditions.size() > 2) {
             SearchCond removed = conditions.remove(0);
             return getOrCond(removed, getOrCond(conditions));
         } else {
@@ -291,6 +306,10 @@ public class SearchCond extends AbstractSearchCond {
         return roleCond;
     }
 
+    public DynRealmCond getDynRealmCond() {
+        return dynRealmCond;
+    }
+
     public ResourceCond getResourceCond() {
         return resourceCond;
     }
@@ -326,7 +345,7 @@ public class SearchCond extends AbstractSearchCond {
         switch (type) {
             case LEAF:
             case NOT_LEAF:
-                isValid = (anyTypeCond != null || anyCond != null || attributeCond != null
+                isValid = (anyTypeCond != null || anyCond != null || attributeCond != null || dynRealmCond != null
                         || relationshipCond != null || relationshipTypeCond != null || membershipCond != null
                         || roleCond != null || resourceCond != null || assignableCond != null || memberCond != null)
                         && (anyTypeCond == null || anyTypeCond.isValid())

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java
new file mode 100644
index 0000000..e8be2aa
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DynRealm.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+public interface DynRealm extends DynMembership<Any<?>>, ProvidedKeyEntity {
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
index 441f87a..68a1e9c 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
@@ -30,6 +30,10 @@ public interface Role extends ProvidedKeyEntity {
 
     List<? extends Realm> getRealms();
 
+    boolean add(DynRealm dynRealm);
+
+    List<? extends DynRealm> getDynRealms();
+
     DynRoleMembership getDynMembership();
 
     void setDynMembership(DynRoleMembership dynMembership);

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
index bd95fe8..ce607bf 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
@@ -37,6 +37,7 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
@@ -143,10 +144,16 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
 
                         case ROLES:
                             RoleCond roleCond = new RoleCond();
-                            roleCond.setRoleKey(value);
+                            roleCond.setRole(value);
                             leaf = SearchCond.getLeafCond(roleCond);
                             break;
 
+                        case DYNREALMS:
+                            DynRealmCond dynRealmCond = new DynRealmCond();
+                            dynRealmCond.setDynRealm(value);
+                            leaf = SearchCond.getLeafCond(dynRealmCond);
+                            break;
+
                         case ASSIGNABLE:
                             AssignableCond assignableCond = new AssignableCond();
                             assignableCond.setRealmFullPath(realm);

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/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 144d42d..08b2f12 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
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.api.search;
 
 import static org.junit.Assert.assertEquals;
 
+import java.util.UUID;
 import org.apache.syncope.common.lib.search.AnyObjectFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.search.GroupFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.search.SpecialAttr;
@@ -32,6 +33,7 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
@@ -109,7 +111,8 @@ public class SearchCondConverterTest {
 
     @Test
     public void nilike() {
-        String fiqlExpression = new UserFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("ros*").query();
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("ros*").
+                query();
         assertEquals("username!~ros*", fiqlExpression);
 
         AttributeCond attrCond = new AnyCond(AttributeCond.Type.ILIKE);
@@ -193,13 +196,26 @@ public class SearchCondConverterTest {
         assertEquals(SpecialAttr.ROLES + "==User reviewer", fiqlExpression);
 
         RoleCond roleCond = new RoleCond();
-        roleCond.setRoleKey("User reviewer");
+        roleCond.setRole("User reviewer");
         SearchCond simpleCond = SearchCond.getLeafCond(roleCond);
 
         assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
     }
 
     @Test
+    public void dynRealms() {
+        String dynRealm = UUID.randomUUID().toString();
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().inDynRealms(dynRealm).query();
+        assertEquals(SpecialAttr.DYNREALMS + "==" + dynRealm, fiqlExpression);
+
+        DynRealmCond dynRealmCond = new DynRealmCond();
+        dynRealmCond.setDynRealm(dynRealm);
+        SearchCond simpleCond = SearchCond.getLeafCond(dynRealmCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
     public void resources() {
         String fiqlExpression = new UserFiqlSearchConditionBuilder().hasResources("resource-ldap").query();
         assertEquals(SpecialAttr.RESOURCES + "==resource-ldap", fiqlExpression);

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/pom.xml b/core/persistence-jpa/pom.xml
index 23d33c7..896eb78 100644
--- a/core/persistence-jpa/pom.xml
+++ b/core/persistence-jpa/pom.xml
@@ -140,6 +140,24 @@ under the License.
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.seleniumhq.selenium</groupId>
+      <artifactId>selenium-java</artifactId>
+      <scope>test</scope>
+      <version>2.44.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.opera</groupId>
+      <artifactId>operadriver</artifactId>
+      <scope>test</scope>
+      <version>1.5</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.seleniumhq.selenium</groupId>
+          <artifactId>selenium-remote-driver</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
index 0c4fdcb..89d27e9 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import static org.apache.syncope.core.persistence.jpa.dao.AbstractDAO.LOG;
+
 import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -41,6 +43,7 @@ import org.apache.syncope.core.persistence.api.dao.AllowedSchemas;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
@@ -51,6 +54,7 @@ import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.Schema;
@@ -80,6 +84,8 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
 
     private AnySearchDAO searchDAO;
 
+    private DynRealmDAO dynRealmDAO;
+
     private AnyUtils anyUtils;
 
     private PlainSchemaDAO plainSchemaDAO() {
@@ -109,6 +115,15 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
         return searchDAO;
     }
 
+    protected DynRealmDAO dynRealmDAO() {
+        synchronized (this) {
+            if (dynRealmDAO == null) {
+                dynRealmDAO = ApplicationContextProvider.getApplicationContext().getBean(DynRealmDAO.class);
+            }
+        }
+        return dynRealmDAO;
+    }
+
     protected abstract AnyUtils init();
 
     protected AnyUtils anyUtils() {
@@ -539,4 +554,28 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
 
         delete(any);
     }
+
+    @Transactional(readOnly = true)
+    @Override
+    public List<String> findDynRealms(final String key) {
+        Query query = entityManager().createNativeQuery(
+                "SELECT dynRealm_id FROM " + JPADynRealmDAO.DYNMEMB_TABLE + " WHERE any_id=?");
+        query.setParameter(1, key);
+
+        List<String> result = new ArrayList<>();
+        for (Object resultKey : query.getResultList()) {
+            String actualKey = resultKey instanceof Object[]
+                    ? (String) ((Object[]) resultKey)[0]
+                    : ((String) resultKey);
+
+            DynRealm dynRealm = dynRealmDAO().find(actualKey);
+            if (dynRealm == null) {
+                LOG.error("Could not find dynRealm with id {}, even though returned by the native query", actualKey);
+            } else if (!result.contains(dynRealm)) {
+                result.add(actualKey);
+            }
+        }
+        return result;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
index d9b4e95..7c1a1b0 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
@@ -29,6 +29,8 @@ import javax.persistence.Entity;
 import javax.validation.ValidationException;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.SerializationUtils;
 import org.apache.commons.lang3.tuple.Pair;
@@ -38,6 +40,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
@@ -45,6 +48,7 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
@@ -70,6 +74,9 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
     protected RealmDAO realmDAO;
 
     @Autowired
+    protected DynRealmDAO dynRealmDAO;
+
+    @Autowired
     protected AnyObjectDAO anyObjectDAO;
 
     @Autowired
@@ -92,6 +99,21 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
         return search(SearchCond.getLeafCond(assignableCond), kind);
     }
 
+    protected SearchCond buildEffectiveCond(final SearchCond cond, final Set<String> dynRealmKeys) {
+        List<SearchCond> effectiveConds = CollectionUtils.collect(dynRealmKeys, new Transformer<String, SearchCond>() {
+
+            @Override
+            public SearchCond transform(final String input) {
+                DynRealmCond dynRealmCond = new DynRealmCond();
+                dynRealmCond.setDynRealm(input);
+                return SearchCond.getLeafCond(dynRealmCond);
+            }
+        }, new ArrayList<SearchCond>());
+        effectiveConds.add(cond);
+
+        return SearchCond.getAndCond(effectiveConds);
+    }
+
     protected abstract int doCount(Set<String> adminRealms, SearchCond cond, AnyTypeKind kind);
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/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 d010e4b..e0545d2 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
@@ -143,6 +143,9 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
                 return anyObject.getRealm().getFullPath().startsWith(realm);
             }
         });
+        if (!authorized) {
+            authorized = !CollectionUtils.intersection(findDynRealms(anyObject.getKey()), authRealms).isEmpty();
+        }
         if (authRealms == null || authRealms.isEmpty() || !authorized) {
             throw new DelegatedAdministrationException(AnyTypeKind.ANY_OBJECT, anyObject.getKey());
         }
@@ -213,6 +216,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
         publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged, AuthContextUtils.getDomain()));
 
         groupDAO().refreshDynMemberships(merged);
+        dynRealmDAO().refreshDynMemberships(merged);
 
         return merged;
     }
@@ -238,6 +242,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     @Override
     public void delete(final AnyObject anyObject) {
         groupDAO().removeDynMemberships(anyObject);
+        dynRealmDAO().removeDynMemberships(anyObject.getKey());
 
         for (ARelationship relationship : findARelationships(anyObject)) {
             relationship.getLeftEnd().getRelationships().remove(relationship);
@@ -259,16 +264,16 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
-    public List<Group> findDynGroups(final AnyObject anyObject) {
+    public List<Group> findDynGroups(final String key) {
         Query query = entityManager().createNativeQuery(
                 "SELECT group_id FROM " + JPAGroupDAO.ADYNMEMB_TABLE + " WHERE any_id=?");
-        query.setParameter(1, anyObject.getKey());
+        query.setParameter(1, key);
 
         List<Group> result = new ArrayList<>();
-        for (Object key : query.getResultList()) {
-            String actualKey = key instanceof Object[]
-                    ? (String) ((Object[]) key)[0]
-                    : ((String) key);
+        for (Object resultKey : query.getResultList()) {
+            String actualKey = resultKey instanceof Object[]
+                    ? (String) ((Object[]) resultKey)[0]
+                    : ((String) resultKey);
 
             Group group = groupDAO().find(actualKey);
             if (group == null) {
@@ -291,7 +296,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
                         return input.getRightEnd();
                     }
                 }, new ArrayList<Group>()),
-                findDynGroups(anyObject));
+                findDynGroups(anyObject.getKey()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index 61d4a7f..cc44ef8 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -44,11 +44,13 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.Realm;
@@ -61,21 +63,34 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
 
     private static final String EMPTY_QUERY = "SELECT any_id FROM user_search_attr WHERE 1=2";
 
-    private String getAdminRealmsFilter(
+    private Pair<String, Set<String>> getAdminRealmsFilter(
             final Set<String> adminRealms,
             final SearchSupport svs,
             final List<Object> parameters) {
 
         Set<String> realmKeys = new HashSet<>();
+        Set<String> dynRealmKeys = new HashSet<>();
         for (String realmPath : RealmUtils.normalize(adminRealms)) {
-            Realm realm = realmDAO.findByFullPath(realmPath);
-            if (realm == null) {
-                LOG.warn("Ignoring invalid realm {}", realmPath);
+            if (realmPath.startsWith("/")) {
+                Realm realm = realmDAO.findByFullPath(realmPath);
+                if (realm == null) {
+                    LOG.warn("Ignoring invalid realm {}", realmPath);
+                } else {
+                    CollectionUtils.collect(
+                            realmDAO.findDescendants(realm), EntityUtils.<Realm>keyTransformer(), realmKeys);
+                }
             } else {
-                CollectionUtils.collect(
-                        realmDAO.findDescendants(realm), EntityUtils.<Realm>keyTransformer(), realmKeys);
+                DynRealm dynRealm = dynRealmDAO.find(realmPath);
+                if (dynRealm == null) {
+                    LOG.warn("Ignoring invalid dynamic realm {}", realmPath);
+                } else {
+                    dynRealmKeys.add(dynRealm.getKey());
+                }
             }
         }
+        if (!dynRealmKeys.isEmpty()) {
+            CollectionUtils.collect(realmDAO.findAll(), EntityUtils.keyTransformer(), realmKeys);
+        }
 
         StringBuilder adminRealmFilter = new StringBuilder("u.any_id IN (").
                 append("SELECT any_id FROM ").append(svs.field().name).
@@ -94,20 +109,23 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
 
         adminRealmFilter.append("))");
 
-        return adminRealmFilter.toString();
+        return Pair.of(adminRealmFilter.toString(), dynRealmKeys);
     }
 
     @Override
     protected int doCount(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) {
         List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
 
-        // 1. get the query string from the search condition
         SearchSupport svs = new SearchSupport(kind);
-        StringBuilder queryString = getQuery(cond, parameters, svs);
+
+        Pair<String, Set<String>> filter = getAdminRealmsFilter(adminRealms, svs, parameters);
+
+        // 1. get the query string from the search condition
+        StringBuilder queryString = getQuery(buildEffectiveCond(cond, filter.getRight()), parameters, svs);
 
         // 2. take into account administrative realms
         queryString.insert(0, "SELECT u.any_id FROM (");
-        queryString.append(") u WHERE ").append(getAdminRealmsFilter(adminRealms, svs, parameters));
+        queryString.append(") u WHERE ").append(filter.getLeft());
 
         // 3. prepare the COUNT query
         queryString.insert(0, "SELECT COUNT(any_id) FROM (");
@@ -132,9 +150,12 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
         try {
             List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
 
-            // 1. get the query string from the search condition
             SearchSupport svs = new SearchSupport(kind);
-            StringBuilder queryString = getQuery(cond, parameters, svs);
+
+            Pair<String, Set<String>> filter = getAdminRealmsFilter(adminRealms, svs, parameters);
+
+            // 1. get the query string from the search condition
+            StringBuilder queryString = getQuery(buildEffectiveCond(cond, filter.getRight()), parameters, svs);
 
             // 2. take into account realms and ordering
             OrderBySupport obs = parseOrderBy(kind, svs, orderBy);
@@ -146,7 +167,7 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
                 queryString.append(')').append(buildWhere(svs, obs));
             }
             queryString.
-                    append(getAdminRealmsFilter(adminRealms, svs, parameters)).
+                    append(filter.getLeft()).
                     append(buildOrderBy(obs));
 
             // 3. prepare the search query
@@ -340,6 +361,9 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
                 } else if (cond.getRoleCond() != null && AnyTypeKind.USER == svs.anyTypeKind) {
                     query.append(getQuery(cond.getRoleCond(),
                             cond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
+                } else if (cond.getDynRealmCond() != null) {
+                    query.append(getQuery(cond.getDynRealmCond(),
+                            cond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
                 } else if (cond.getMemberCond() != null && AnyTypeKind.GROUP == svs.anyTypeKind) {
                     query.append(getQuery(cond.getMemberCond(),
                             cond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
@@ -503,7 +527,7 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
 
         query.append("SELECT DISTINCT any_id FROM ").
                 append(svs.role().name).append(" WHERE ").
-                append("role_id=?").append(setParameter(parameters, cond.getRoleKey())).
+                append("role_id=?").append(setParameter(parameters, cond.getRole())).
                 append(") ");
 
         if (not) {
@@ -514,7 +538,27 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
 
         query.append("SELECT DISTINCT any_id FROM ").
                 append(svs.dynrolemembership().name).append(" WHERE ").
-                append("role_id=?").append(setParameter(parameters, cond.getRoleKey())).
+                append("role_id=?").append(setParameter(parameters, cond.getRole())).
+                append("))");
+
+        return query.toString();
+    }
+
+    private String getQuery(
+            final DynRealmCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
+
+        StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+                append(svs.field().name).append(" WHERE (");
+
+        if (not) {
+            query.append("any_id NOT IN (");
+        } else {
+            query.append("any_id IN (");
+        }
+
+        query.append("SELECT DISTINCT any_id FROM ").
+                append(svs.dynrealmmembership().name).append(" WHERE ").
+                append("dynRealm_id=?").append(setParameter(parameters, cond.getDynRealm())).
                 append("))");
 
         return query.toString();

http://git-wip-us.apache.org/repos/asf/syncope/blob/062c87a2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java
new file mode 100644
index 0000000..874f0d3
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.util.List;
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.persistence.jpa.entity.JPADynRealm;
+import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+@Repository
+public class JPADynRealmDAO extends AbstractDAO<DynRealm> implements DynRealmDAO {
+
+    public static final String DYNMEMB_TABLE = "DynRealmMembers";
+
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
+    private AnySearchDAO searchDAO;
+
+    private AnySearchDAO searchDAO() {
+        synchronized (this) {
+            if (searchDAO == null) {
+                searchDAO = ApplicationContextProvider.getApplicationContext().getBean(AnySearchDAO.class);
+            }
+        }
+        return searchDAO;
+    }
+
+    @Override
+    public DynRealm find(final String key) {
+        return entityManager().find(JPADynRealm.class, key);
+    }
+
+    @Override
+    public List<DynRealm> findAll() {
+        TypedQuery<DynRealm> query = entityManager().createQuery(
+                "SELECT e FROM " + JPADynRealm.class.getSimpleName() + " e ", DynRealm.class);
+        return query.getResultList();
+    }
+
+    @Override
+    public DynRealm save(final DynRealm dynRealm) {
+        DynRealm merged = entityManager().merge(dynRealm);
+
+        // refresh dynamic memberships
+        if (merged.getFIQLCond() != null) {
+            clearDynMembers(merged);
+
+            List<Any<?>> matching = searchDAO().search(
+                    SearchCondConverter.convert(merged.getFIQLCond()), AnyTypeKind.USER);
+            for (Any<?> any : matching) {
+                Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
+                insert.setParameter(1, any.getKey());
+                insert.setParameter(2, merged.getKey());
+                insert.executeUpdate();
+
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, any, AuthContextUtils.getDomain()));
+            }
+
+            matching = searchDAO().search(
+                    SearchCondConverter.convert(merged.getFIQLCond()), AnyTypeKind.GROUP);
+            for (Any<?> any : matching) {
+                Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
+                insert.setParameter(1, any.getKey());
+                insert.setParameter(2, merged.getKey());
+                insert.executeUpdate();
+
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, any, AuthContextUtils.getDomain()));
+            }
+
+            matching = searchDAO().search(
+                    SearchCondConverter.convert(merged.getFIQLCond()), AnyTypeKind.ANY_OBJECT);
+            for (Any<?> any : matching) {
+                Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
+                insert.setParameter(1, any.getKey());
+                insert.setParameter(2, merged.getKey());
+                insert.executeUpdate();
+
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, any, AuthContextUtils.getDomain()));
+            }
+        }
+
+        return merged;
+    }
+
+    @Override
+    public void delete(final String key) {
+        DynRealm dynRealm = find(key);
+        if (dynRealm == null) {
+            return;
+        }
+
+        clearDynMembers(dynRealm);
+
+        entityManager().remove(dynRealm);
+    }
+
+    @Override
+    public void clearDynMembers(final DynRealm dynRealm) {
+        Query delete = entityManager().createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE dynRealm_id=?");
+        delete.setParameter(1, dynRealm.getKey());
+        delete.executeUpdate();
+    }
+
+    @Transactional
+    @Override
+    public void refreshDynMemberships(final Any<?> any) {
+        for (DynRealm dynRealm : findAll()) {
+            if (dynRealm.getFIQLCond() != null) {
+                Query delete = entityManager().createNativeQuery(
+                        "DELETE FROM " + DYNMEMB_TABLE + " WHERE dynRealm_id=? AND any_id=?");
+                delete.setParameter(1, dynRealm.getKey());
+                delete.setParameter(2, any.getKey());
+                delete.executeUpdate();
+
+                if (searchDAO().matches(any, SearchCondConverter.convert(dynRealm.getFIQLCond()))) {
+                    Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
+                    insert.setParameter(1, any.getKey());
+                    insert.setParameter(2, dynRealm.getKey());
+                    insert.executeUpdate();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void removeDynMemberships(final String key) {
+        Query delete = entityManager().createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE any_id=?");
+        delete.setParameter(1, key);
+        delete.executeUpdate();
+    }
+
+}


[3/4] syncope git commit: [SYNCOPE-1067] Core modifications

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 1b7374a..6cec27a 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
@@ -28,6 +28,7 @@ import java.util.Set;
 import javax.persistence.NoResultException;
 import javax.persistence.Query;
 import javax.persistence.TypedQuery;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -165,6 +166,10 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
                         || realm.equals(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey()));
             }
         });
+        if (!authorized) {
+            authorized = !CollectionUtils.intersection(findDynRealms(group.getKey()), authRealms).isEmpty();
+        }
+
         if (authRealms == null || authRealms.isEmpty() || !authorized) {
             throw new DelegatedAdministrationException(AnyTypeKind.GROUP, group.getKey());
         }
@@ -315,11 +320,15 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             }
         }
 
+        dynRealmDAO().refreshDynMemberships(merged);
+
         return merged;
     }
 
     @Override
     public void delete(final Group group) {
+        dynRealmDAO().removeDynMemberships(group.getKey());
+
         for (AMembership membership : findAMemberships(group)) {
             AnyObject leftEnd = membership.getLeftEnd();
             leftEnd.getMemberships().remove(membership);
@@ -349,6 +358,9 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd, AuthContextUtils.getDomain()));
         }
 
+        clearUDynMembers(group);
+        clearADynMembers(group);
+
         entityManager().remove(group);
         publisher.publishEvent(
                 new AnyDeletedEvent(this, AnyTypeKind.GROUP, group.getKey(), AuthContextUtils.getDomain()));
@@ -427,7 +439,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
 
     @Override
     public void removeDynMemberships(final AnyObject anyObject) {
-        List<Group> dynGroups = anyObjectDAO().findDynGroups(anyObject);
+        List<Group> dynGroups = anyObjectDAO().findDynGroups(anyObject.getKey());
 
         Query delete = entityManager().createNativeQuery("DELETE FROM " + ADYNMEMB_TABLE + " WHERE any_id=?");
         delete.setParameter(1, anyObject.getKey());
@@ -501,7 +513,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
 
     @Override
     public void removeDynMemberships(final User user) {
-        List<Group> dynGroups = userDAO().findDynGroups(user);
+        List<Group> dynGroups = userDAO().findDynGroups(user.getKey());
 
         Query delete = entityManager().createNativeQuery("DELETE FROM " + UDYNMEMB_TABLE + " WHERE any_id=?");
         delete.setParameter(1, user.getKey());

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 9796f4d..367ee8d 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
@@ -121,6 +121,8 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
             publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user, AuthContextUtils.getDomain()));
         }
 
+        clearDynMembers(role);
+
         entityManager().remove(role);
     }
 
@@ -183,9 +185,9 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
     }
 
     @Override
-    public void removeDynMemberships(final User user) {
+    public void removeDynMemberships(final String key) {
         Query delete = entityManager().createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE any_id=?");
-        delete.setParameter(1, user.getKey());
+        delete.setParameter(1, key);
         delete.executeUpdate();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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 40db0c4..afa83f4 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
@@ -186,6 +186,9 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                     return user.getRealm().getFullPath().startsWith(realm);
                 }
             });
+            if (!authorized) {
+                authorized = !CollectionUtils.intersection(findDynRealms(user.getKey()), authRealms).isEmpty();
+            }
             if (authRealms == null || authRealms.isEmpty() || !authorized) {
                 throw new DelegatedAdministrationException(AnyTypeKind.USER, user.getKey());
             }
@@ -448,14 +451,16 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
         roleDAO.refreshDynMemberships(merged);
         groupDAO().refreshDynMemberships(merged);
+        dynRealmDAO().refreshDynMemberships(merged);
 
         return merged;
     }
 
     @Override
     public void delete(final User user) {
-        roleDAO.removeDynMemberships(user);
-        groupDAO.removeDynMemberships(user);
+        roleDAO.removeDynMemberships(user.getKey());
+        groupDAO().removeDynMemberships(user);
+        dynRealmDAO().removeDynMemberships(user.getKey());
 
         AccessToken accessToken = accessTokenDAO.findByOwner(user.getUsername());
         if (accessToken != null) {
@@ -470,21 +475,21 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
     public Collection<Role> findAllRoles(final User user) {
-        return CollectionUtils.union(user.getRoles(), findDynRoles(user));
+        return CollectionUtils.union(user.getRoles(), findDynRoles(user.getKey()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
-    public List<Role> findDynRoles(final User user) {
+    public List<Role> findDynRoles(final String key) {
         Query query = entityManager().createNativeQuery(
                 "SELECT role_id FROM " + JPARoleDAO.DYNMEMB_TABLE + " WHERE any_id=?");
-        query.setParameter(1, user.getKey());
+        query.setParameter(1, key);
 
         List<Role> result = new ArrayList<>();
-        for (Object key : query.getResultList()) {
-            String actualKey = key instanceof Object[]
-                    ? (String) ((Object[]) key)[0]
-                    : ((String) key);
+        for (Object resultKey : query.getResultList()) {
+            String actualKey = resultKey instanceof Object[]
+                    ? (String) ((Object[]) resultKey)[0]
+                    : ((String) resultKey);
 
             Role role = roleDAO.find(actualKey);
             if (role == null) {
@@ -498,16 +503,16 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
-    public List<Group> findDynGroups(final User user) {
+    public List<Group> findDynGroups(final String key) {
         Query query = entityManager().createNativeQuery(
                 "SELECT group_id FROM " + JPAGroupDAO.UDYNMEMB_TABLE + " WHERE any_id=?");
-        query.setParameter(1, user.getKey());
+        query.setParameter(1, key);
 
         List<Group> result = new ArrayList<>();
-        for (Object key : query.getResultList()) {
-            String actualKey = key instanceof Object[]
-                    ? (String) ((Object[]) key)[0]
-                    : ((String) key);
+        for (Object resultKey : query.getResultList()) {
+            String actualKey = resultKey instanceof Object[]
+                    ? (String) ((Object[]) resultKey)[0]
+                    : ((String) resultKey);
 
             Group group = groupDAO().find(actualKey);
             if (group == null) {
@@ -530,7 +535,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                         return input.getRightEnd();
                     }
                 }, new ArrayList<Group>()),
-                findDynGroups(user));
+                findDynGroups(user.getKey()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
index fd40470..5cac5bb 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
@@ -135,6 +135,10 @@ class SearchSupport {
         return new SearchView("svdr", JPARoleDAO.DYNMEMB_TABLE);
     }
 
+    public SearchView dynrealmmembership() {
+        return new SearchView("svdrealm", JPADynRealmDAO.DYNMEMB_TABLE);
+    }
+
     public SearchView nullAttr() {
         return new SearchView("svna", field().name + "_null_attr");
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java
new file mode 100644
index 0000000..3a0aaa1
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import javax.persistence.Cacheable;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.jpa.validation.entity.DynRealmCheck;
+
+@Entity
+@Table(name = JPADynRealm.TABLE)
+@Cacheable
+@DynRealmCheck
+public class JPADynRealm extends AbstractProvidedKeyEntity implements DynRealm {
+
+    private static final long serialVersionUID = -6851035842423560341L;
+
+    public static final String TABLE = "DynRealm";
+
+    @NotNull
+    private String fiql;
+
+    @Override
+    public String getFIQLCond() {
+        return fiql;
+    }
+
+    @Override
+    public void setFIQLCond(final String fiql) {
+        this.fiql = fiql;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 7d9660c..daea67a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -124,6 +124,7 @@ import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAOrgUnit;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 
 @Component
 public class JPAEntityFactory implements EntityFactory {
@@ -137,6 +138,8 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPADomain();
         } else if (reference.equals(Realm.class)) {
             result = (E) new JPARealm();
+        } else if (reference.equals(DynRealm.class)) {
+            result = (E) new JPADynRealm();
         } else if (reference.equals(AnyTemplateRealm.class)) {
             result = (E) new JPAAnyTemplateRealm();
         } else if (reference.equals(AccountPolicy.class)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
index 919c3cb..30a1f47 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
@@ -41,6 +41,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.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.validation.entity.RoleCheck;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 
 @Entity
 @Table(name = JPARole.TABLE)
@@ -67,6 +68,14 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @Valid
     private List<JPARealm> realms = new ArrayList<>();
 
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(joinColumns =
+            @JoinColumn(name = "role_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "dynamicRealm_id"))
+    @Valid
+    private List<JPADynRealm> dynRealms = new ArrayList<>();
+
     @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "role")
     @Valid
     private JPADynRoleMembership dynMembership;
@@ -91,6 +100,17 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     }
 
     @Override
+    public boolean add(final DynRealm dynamicRealm) {
+        checkType(dynamicRealm, JPADynRealm.class);
+        return dynRealms.add((JPADynRealm) dynamicRealm);
+    }
+
+    @Override
+    public List<? extends DynRealm> getDynRealms() {
+        return dynRealms;
+    }
+
+    @Override
     public DynRoleMembership getDynMembership() {
         return dynMembership;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java
new file mode 100644
index 0000000..567dc74
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.validation.entity;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = DynRealmValidator.class)
+@Documented
+public @interface DynRealmCheck {
+
+    String message() default "{org.apache.syncope.core.persistence.validation.dynrealm}";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java
new file mode 100644
index 0000000..e97c122
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.validation.entity;
+
+import java.util.regex.Pattern;
+import javax.validation.ConstraintValidatorContext;
+import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+
+public class DynRealmValidator extends AbstractValidator<DynRealmCheck, DynRealm> {
+
+    private static final Pattern REALM_KEY_PATTERN = Pattern.compile("^[A-Za-z0-9]+");
+
+    @Override
+    public boolean isValid(final DynRealm dynRealm, final ConstraintValidatorContext context) {
+        context.disableDefaultConstraintViolation();
+
+        boolean isValid = true;
+
+        if (dynRealm.getKey().startsWith("/") || !REALM_KEY_PATTERN.matcher(dynRealm.getKey()).matches()) {
+            isValid = false;
+
+            context.buildConstraintViolationWithTemplate(
+                    getTemplate(EntityViolationType.InvalidDynRealm,
+                            "Only letters and numbers are allowed in dynamic realm key, and must not start with /")).
+                    addPropertyNode("key").addConstraintViolation();
+        }
+
+        return isValid;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
index e1bdf68..3b46f62 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
@@ -28,6 +28,7 @@ import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntit
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.entity.AnnotatedEntity;
 import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.DynMembership;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.GroupableRelatable;
 import org.apache.syncope.core.persistence.api.entity.Policy;
@@ -62,6 +63,7 @@ public class EntityValidationListener {
                         && !Policy.class.equals(interf)
                         && !GroupableRelatable.class.equals(interf)
                         && !Any.class.equals(interf)
+                        && !DynMembership.class.equals(interf)
                         && Entity.class.isAssignableFrom(interf)) {
 
                     entityInt = interf;

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/resources/indexes.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/indexes.xml b/core/persistence-jpa/src/main/resources/indexes.xml
index 1dfa63d..ded4d52 100644
--- a/core/persistence-jpa/src/main/resources/indexes.xml
+++ b/core/persistence-jpa/src/main/resources/indexes.xml
@@ -29,6 +29,9 @@ under the License.
   <entry key="DynRoleMembers_any_id">CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id)</entry>
   <entry key="DynRoleMembers_role_id">CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id)</entry>
 
+  <entry key="DynRealmMembers_any_id">CREATE INDEX DynRealmMembers_any_id ON DynRealmMembers(any_id)</entry>
+  <entry key="DynRealmMembers_realm_id">CREATE INDEX DynRealmMembers_dynRealm_id ON DynRealmMembers(dynRealm_id)</entry>
+
   <entry key="UPlainAttrValue_stringvalueIndex">CREATE INDEX UAttrValue_stringvalueIndex ON UPlainAttrValue(stringvalue)</entry>
   <entry key="UPlainAttrValue_datevalueIndex">CREATE INDEX UAttrValue_datevalueIndex ON UPlainAttrValue(datevalue)</entry>
   <entry key="UPlainAttrValue_longvalueIndex">CREATE INDEX UAttrValue_longvalueIndex ON UPlainAttrValue(longvalue)</entry>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/main/resources/views.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/views.xml b/core/persistence-jpa/src/main/resources/views.xml
index fb68516..bcff6b9 100644
--- a/core/persistence-jpa/src/main/resources/views.xml
+++ b/core/persistence-jpa/src/main/resources/views.xml
@@ -39,6 +39,12 @@ under the License.
     role_id CHAR(36),
     UNIQUE(any_id, role_id))
   </entry>
+  <entry key="DynRealmMembers">
+    CREATE TABLE DynRealmMembers(
+    any_id CHAR(36),
+    dynRealm_id CHAR(36),
+    UNIQUE(any_id, dynRealm_id))
+  </entry>
 
   <!-- user -->
   <entry key="user_search">

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
index c7e5c62..1ba9913 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
@@ -109,7 +109,7 @@ public class AnySearchTest extends AbstractTest {
         assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(groupCond)));
 
         RoleCond roleCond = new RoleCond();
-        roleCond.setRoleKey("Other");
+        roleCond.setRole("Other");
         assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(roleCond)));
 
         user = userDAO.find("c9b2dec2-00a7-4855-97c0-d854842b4b24");
@@ -303,7 +303,7 @@ public class AnySearchTest extends AbstractTest {
     @Test
     public void searchByRole() {
         RoleCond roleCond = new RoleCond();
-        roleCond.setRoleKey("Other");
+        roleCond.setRole("Other");
 
         List<User> users = searchDAO.search(SearchCond.getLeafCond(roleCond), AnyTypeKind.USER);
         assertNotNull(users);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java
index 174eef2..55fbb19 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.persistence.jpa.inner;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
index f91aa7e..f3d3203 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
@@ -100,7 +100,7 @@ public class AnySearchTest extends AbstractTest {
 
         // 2. search user by this dynamic role
         RoleCond roleCond = new RoleCond();
-        roleCond.setRoleKey(role.getKey());
+        roleCond.setRole(role.getKey());
 
         List<User> users = searchDAO.search(SearchCond.getLeafCond(roleCond), AnyTypeKind.USER);
         assertNotNull(users);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java
new file mode 100644
index 0000000..307b865
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.outer;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class DynRealmTest extends AbstractTest {
+
+    @Autowired
+    private DynRealmDAO dynRealmDAO;
+
+    @Autowired
+    private AnySearchDAO searchDAO;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Test
+    public void misc() {
+        DynRealm dynRealm = entityFactory.newEntity(DynRealm.class);
+        dynRealm.setKey("/name");
+        dynRealm.setFIQLCond("cool==true");
+
+        // invalid key (starts with /)
+        try {
+            dynRealmDAO.save(dynRealm);
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        dynRealm.setKey("name");
+        DynRealm actual = dynRealmDAO.save(dynRealm);
+        assertNotNull(actual);
+
+        dynRealmDAO.flush();
+
+        DynRealmCond dynRealmCond = new DynRealmCond();
+        dynRealmCond.setDynRealm(actual.getKey());
+        List<User> matching = searchDAO.search(SearchCond.getLeafCond(dynRealmCond), AnyTypeKind.USER);
+        assertNotNull(matching);
+        assertFalse(matching.isEmpty());
+
+        User user = matching.get(0);
+        assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(dynRealmCond)));
+
+        assertTrue(userDAO.findDynRealms(user.getKey()).contains(actual.getKey()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java
new file mode 100644
index 0000000..5244b65
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/DynRealmDataBinder.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.data;
+
+import org.apache.syncope.common.lib.to.DynRealmTO;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+
+public interface DynRealmDataBinder {
+
+    DynRealm create(DynRealmTO dynRealmTO);
+
+    DynRealm update(DynRealm dynRealm, DynRealmTO dynRealmTO);
+
+    DynRealmTO getDynRealmTO(DynRealm dynRealm);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
index 5428f5f..550f183 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
@@ -102,6 +102,9 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
                 details);
 
         if (details) {
+            // dynamic realms
+            anyObjectTO.getDynRealms().addAll(userDAO.findDynRealms(anyObject.getKey()));
+
             // relationships
             CollectionUtils.collect(anyObject.getRelationships(), new Transformer<ARelationship, RelationshipTO>() {
 
@@ -126,7 +129,7 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
             }, anyObjectTO.getMemberships());
 
             // dynamic memberships
-            CollectionUtils.collect(anyObjectDAO.findDynGroups(anyObject),
+            CollectionUtils.collect(anyObjectDAO.findDynGroups(anyObject.getKey()),
                     EntityUtils.<Group>keyTransformer(), anyObjectTO.getDynGroups());
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java
new file mode 100644
index 0000000..56d7903
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DynRealmDataBinderImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.DynRealmTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.provisioning.api.data.DynRealmDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DynRealmDataBinderImpl implements DynRealmDataBinder {
+
+    @Autowired
+    private DynRealmDAO dynRealmDAO;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Override
+    public DynRealm create(final DynRealmTO dynRealmTO) {
+        return update(entityFactory.newEntity(DynRealm.class), dynRealmTO);
+    }
+
+    @Override
+    public DynRealm update(final DynRealm dynRealm, final DynRealmTO dynRealmTO) {
+        dynRealm.setKey(dynRealmTO.getKey());
+
+        SearchCond cond = SearchCondConverter.convert(dynRealmTO.getCond());
+        if (!cond.isValid()) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
+            sce.getElements().add(dynRealmTO.getCond());
+            throw sce;
+        }
+        dynRealm.setFIQLCond(dynRealmTO.getCond());
+
+        return dynRealmDAO.save(dynRealm);
+    }
+
+    @Override
+    public DynRealmTO getDynRealmTO(final DynRealm dynRealm) {
+        DynRealmTO dynRealmTO = new DynRealmTO();
+
+        dynRealmTO.setKey(dynRealm.getKey());
+        dynRealmTO.setCond(dynRealm.getFIQLCond());
+
+        return dynRealmTO;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
index 32206aa..c4f8c50 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
@@ -231,7 +231,7 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
         }
 
         group = groupDAO.save(group);
-        
+
         // dynamic membership
         if (groupPatch.getUDynMembershipCond() == null) {
             if (group.getUDynMembership() != null) {
@@ -345,6 +345,11 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
                 group.getResources(),
                 details);
 
+        if (details) {
+            // dynamic realms
+            groupTO.getDynRealms().addAll(groupDAO.findDynRealms(group.getKey()));
+        }
+
         if (group.getUDynMembership() != null) {
             groupTO.setUDynMembershipCond(group.getUDynMembership().getFIQLCond());
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
index 6ccb229..b907051 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
@@ -23,15 +23,18 @@ import org.apache.commons.collections4.Transformer;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.RoleTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
 import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 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.DynRoleMembership;
 import org.apache.syncope.core.provisioning.api.data.RoleDataBinder;
+import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -46,6 +49,9 @@ public class RoleDataBinderImpl implements RoleDataBinder {
     private RealmDAO realmDAO;
 
     @Autowired
+    private DynRealmDAO dynRealmDAO;
+
+    @Autowired
     private RoleDAO roleDAO;
 
     @Autowired
@@ -93,6 +99,16 @@ public class RoleDataBinderImpl implements RoleDataBinder {
             }
         }
 
+        role.getDynRealms().clear();
+        for (String key : roleTO.getDynRealms()) {
+            DynRealm dynRealm = dynRealmDAO.find(key);
+            if (dynRealm == null) {
+                LOG.debug("Invalid dynamic ream {}, ignoring", key);
+            } else {
+                role.add(dynRealm);
+            }
+        }
+
         role = roleDAO.save(role);
 
         // dynamic membership
@@ -127,6 +143,8 @@ public class RoleDataBinderImpl implements RoleDataBinder {
             }
         }, roleTO.getRealms());
 
+        CollectionUtils.collect(role.getDynRealms(), EntityUtils.keyTransformer(), roleTO.getDynRealms());
+
         if (role.getDynMembership() != null) {
             roleTO.setDynMembershipCond(role.getDynMembership().getFIQLCond());
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index 3cd52c3..54c9983 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -606,6 +606,9 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
                 details);
 
         if (details) {
+            // dynamic realms
+            userTO.getDynRealms().addAll(userDAO.findDynRealms(user.getKey()));
+
             // roles
             CollectionUtils.collect(user.getRoles(),
                     EntityUtils.<Role>keyTransformer(), userTO.getRoles());
@@ -634,9 +637,9 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
             }, userTO.getMemberships());
 
             // dynamic memberships
-            CollectionUtils.collect(userDAO.findDynRoles(user),
+            CollectionUtils.collect(userDAO.findDynRoles(user.getKey()),
                     EntityUtils.<Role>keyTransformer(), userTO.getDynRoles());
-            CollectionUtils.collect(userDAO.findDynGroups(user),
+            CollectionUtils.collect(userDAO.findDynGroups(user.getKey()),
                     EntityUtils.<Group>keyTransformer(), userTO.getDynGroups());
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java
new file mode 100644
index 0000000..41e3bad
--- /dev/null
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/DynRealmServiceImpl.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import java.net.URI;
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.DynRealmTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.DynRealmService;
+import org.apache.syncope.core.logic.DynRealmLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class DynRealmServiceImpl extends AbstractServiceImpl implements DynRealmService {
+
+    @Autowired
+    private DynRealmLogic logic;
+
+    @Override
+    public List<DynRealmTO> list() {
+        return logic.list();
+    }
+
+    @Override
+    public DynRealmTO read(final String key) {
+        return logic.read(key);
+    }
+
+    @Override
+    public Response create(final DynRealmTO roleTO) {
+        DynRealmTO created = logic.create(roleTO);
+        URI location = uriInfo.getAbsolutePathBuilder().path(created.getKey()).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, created.getKey()).
+                build();
+    }
+
+    @Override
+    public void update(final DynRealmTO roleTO) {
+        logic.update(roleTO);
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
index aba2e50..79ba01b 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.core.spring.security;
 
 import com.fasterxml.jackson.core.type.TypeReference;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -29,11 +28,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.annotation.Resource;
-import org.apache.commons.collections4.Closure;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.SetUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.SyncopeConstants;
@@ -65,6 +63,7 @@ import org.apache.syncope.core.provisioning.api.ConnectorFactory;
 import org.apache.syncope.core.provisioning.api.EntitlementsHolder;
 import org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
 import org.identityconnectors.framework.common.objects.Uid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -92,6 +91,10 @@ public class AuthDataAccessor {
     protected static final Set<SyncopeGrantedAuthority> ANONYMOUS_AUTHORITIES =
             Collections.singleton(new SyncopeGrantedAuthority(StandardEntitlement.ANONYMOUS));
 
+    protected static final String[] GROUP_OWNER_ENTITLEMENTS = new String[] {
+        StandardEntitlement.GROUP_READ, StandardEntitlement.GROUP_UPDATE, StandardEntitlement.GROUP_DELETE
+    };
+
     @Resource(name = "adminUser")
     protected String adminUser;
 
@@ -282,39 +285,35 @@ public class AuthDataAccessor {
         if (user.isMustChangePassword()) {
             authorities.add(new SyncopeGrantedAuthority(StandardEntitlement.MUST_CHANGE_PASSWORD));
         } else {
-            final Map<String, Set<String>> entForRealms = new HashMap<>();
-
-            // Give entitlements as assigned by roles (with realms, where applicable) - assigned either
-            // statically and dynamically
-            for (final Role role : userDAO.findAllRoles(user)) {
-                IterableUtils.forEach(role.getEntitlements(), new Closure<String>() {
-
-                    @Override
-                    public void execute(final String entitlement) {
-                        Set<String> realms = entForRealms.get(entitlement);
-                        if (realms == null) {
-                            realms = new HashSet<>();
-                            entForRealms.put(entitlement, realms);
-                        }
+            Map<String, Set<String>> entForRealms = new HashMap<>();
+
+            // Give entitlements as assigned by roles (with static or dynamic realms, where applicable) - assigned
+            // either statically and dynamically
+            for (Role role : userDAO.findAllRoles(user)) {
+                for (String entitlement : role.getEntitlements()) {
+                    Set<String> realms = entForRealms.get(entitlement);
+                    if (realms == null) {
+                        realms = new HashSet<>();
+                        entForRealms.put(entitlement, realms);
+                    }
+
+                    CollectionUtils.collect(role.getRealms(), new Transformer<Realm, String>() {
 
-                        CollectionUtils.collect(role.getRealms(), new Transformer<Realm, String>() {
+                        @Override
+                        public String transform(final Realm realm) {
+                            return realm.getFullPath();
+                        }
+                    }, realms);
 
-                            @Override
-                            public String transform(final Realm realm) {
-                                return realm.getFullPath();
-                            }
-                        }, realms);
+                    if (!entitlement.endsWith("_CREATE") && !entitlement.endsWith("_DELETE")) {
+                        CollectionUtils.collect(role.getDynRealms(), EntityUtils.keyTransformer(), realms);
                     }
-                });
+                }
             }
 
             // Give group entitlements for owned groups
             for (Group group : groupDAO.findOwnedByUser(user.getKey())) {
-                for (String entitlement : Arrays.asList(
-                        StandardEntitlement.GROUP_READ,
-                        StandardEntitlement.GROUP_UPDATE,
-                        StandardEntitlement.GROUP_DELETE)) {
-
+                for (String entitlement : GROUP_OWNER_ENTITLEMENTS) {
                     Set<String> realms = entForRealms.get(entitlement);
                     if (realms == null) {
                         realms = new HashSet<>();
@@ -376,7 +375,7 @@ public class AuthDataAccessor {
                         + " for JWT " + authentication.getClaims().getTokenId());
             }
 
-            if (user.isSuspended() != null && user.isSuspended()) {
+            if (BooleanUtils.isTrue(user.isSuspended())) {
                 throw new DisabledException("User " + user.getUsername() + " is suspended");
             }
 
@@ -385,13 +384,9 @@ public class AuthDataAccessor {
                 throw new DisabledException("User " + user.getUsername() + " not allowed to authenticate");
             }
 
-            if (user.isMustChangePassword()) {
+            if (BooleanUtils.isTrue(user.isMustChangePassword())) {
                 authorities = Collections.singleton(
                         new SyncopeGrantedAuthority(StandardEntitlement.MUST_CHANGE_PASSWORD));
-            } else if (accessToken.getAuthorities() == null) {
-                LOG.debug("No authorities found in JWT, calculating...");
-
-                authorities = getUserAuthorities(user);
             } else {
                 LOG.debug("Authorities found in JWT, fetching...");
 
@@ -400,8 +395,8 @@ public class AuthDataAccessor {
                             ENCRYPTOR.decode(new String(accessToken.getAuthorities()), CipherAlgorithm.AES),
                             new TypeReference<Set<SyncopeGrantedAuthority>>() {
                     });
-                } catch (Exception e) {
-                    LOG.error("Could not read stored authorities", e);
+                } catch (Throwable t) {
+                    LOG.error("Could not read stored authorities", t);
                     authorities = Collections.emptySet();
                 }
             }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
index 47bcf9d..d9ce84f 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
@@ -103,7 +103,13 @@ public class ElasticsearchUtils {
                                 ? userDAO.findAllResourceKeys(any.getKey())
                                 : any instanceof AnyObject
                                         ? anyObjectDAO.findAllResourceKeys(any.getKey())
-                                        : any.getResourceKeys());
+                                        : any.getResourceKeys()).
+                field("dynRealms",
+                        any instanceof User
+                                ? userDAO.findDynRealms(any.getKey())
+                                : any instanceof AnyObject
+                                        ? anyObjectDAO.findDynRealms(any.getKey())
+                                        : groupDAO.findDynRealms(any.getKey()));
 
         if (any instanceof AnyObject) {
             AnyObject anyObject = ((AnyObject) any);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
index 0318bc5..82b7c6f 100644
--- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
+++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
@@ -18,9 +18,12 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import static org.apache.syncope.core.persistence.jpa.dao.AbstractDAO.LOG;
+
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.apache.commons.collections4.CollectionUtils;
@@ -34,6 +37,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
@@ -44,6 +48,7 @@ import org.apache.syncope.core.persistence.api.dao.search.RoleCond;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.Realm;
@@ -75,21 +80,37 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
     @Autowired
     private ElasticsearchUtils elasticsearchUtils;
 
-    private DisMaxQueryBuilder adminRealmsFilter(final Set<String> adminRealms) {
+    private Pair<DisMaxQueryBuilder, Set<String>> adminRealmsFilter(final Set<String> adminRealms) {
         DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
 
+        Set<String> dynRealmKeys = new HashSet<>();
         for (String realmPath : RealmUtils.normalize(adminRealms)) {
-            Realm realm = realmDAO.findByFullPath(realmPath);
-            if (realm == null) {
-                LOG.warn("Ignoring invalid realm {}", realmPath);
+            if (realmPath.startsWith("/")) {
+                Realm realm = realmDAO.findByFullPath(realmPath);
+                if (realm == null) {
+                    LOG.warn("Ignoring invalid realm {}", realmPath);
+                } else {
+                    for (Realm descendant : realmDAO.findDescendants(realm)) {
+                        builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath()));
+                    }
+                }
             } else {
-                for (Realm descendant : realmDAO.findDescendants(realm)) {
-                    builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath()));
+                DynRealm dynRealm = dynRealmDAO.find(realmPath);
+                if (dynRealm == null) {
+                    LOG.warn("Ignoring invalid dynamic realm {}", realmPath);
+                } else {
+                    dynRealmKeys.add(dynRealm.getKey());
+                    builder.add(QueryBuilders.termQuery("dynRealm", dynRealm.getKey()));
                 }
             }
         }
+        if (!dynRealmKeys.isEmpty()) {
+            for (Realm descendant : realmDAO.findAll()) {
+                builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath()));
+            }
+        }
 
-        return builder;
+        return Pair.of(builder, dynRealmKeys);
     }
 
     private SearchRequestBuilder searchRequestBuilder(
@@ -97,14 +118,16 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
             final SearchCond cond,
             final AnyTypeKind kind) {
 
+        Pair<DisMaxQueryBuilder, Set<String>> filter = adminRealmsFilter(adminRealms);
+
         return client.prepareSearch(AuthContextUtils.getDomain().toLowerCase()).
                 setTypes(kind.name()).
                 setSearchType(SearchType.QUERY_THEN_FETCH).
                 setQuery(SyncopeConstants.FULL_ADMIN_REALMS.equals(adminRealms)
                         ? getQueryBuilder(cond, kind)
                         : QueryBuilders.boolQuery().
-                                must(adminRealmsFilter(adminRealms)).
-                                must(getQueryBuilder(cond, kind)));
+                                must(filter.getLeft()).
+                                must(getQueryBuilder(buildEffectiveCond(cond, filter.getRight()), kind)));
     }
 
     @Override
@@ -196,6 +219,8 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
                     builder = getQueryBuilder(cond.getAssignableCond());
                 } else if (cond.getRoleCond() != null && AnyTypeKind.USER == kind) {
                     builder = getQueryBuilder(cond.getRoleCond());
+                } else if (cond.getDynRealmCond() != null) {
+                    builder = getQueryBuilder(cond.getDynRealmCond());
                 } else if (cond.getMemberCond() != null && AnyTypeKind.GROUP == kind) {
                     builder = getQueryBuilder(cond.getMemberCond());
                 } else if (cond.getResourceCond() != null) {
@@ -286,7 +311,11 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
     }
 
     private QueryBuilder getQueryBuilder(final RoleCond cond) {
-        return QueryBuilders.termQuery("roles", cond.getRoleKey());
+        return QueryBuilders.termQuery("roles", cond.getRole());
+    }
+
+    private QueryBuilder getQueryBuilder(final DynRealmCond cond) {
+        return QueryBuilders.termQuery("dynRealms", cond.getDynRealm());
     }
 
     private QueryBuilder getQueryBuilder(final MemberCond cond) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 1ef0d32..f22a045 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -66,6 +66,7 @@ import org.apache.syncope.common.rest.api.service.CamelRouteService;
 import org.apache.syncope.common.rest.api.service.ConfigurationService;
 import org.apache.syncope.common.rest.api.service.ConnectorService;
 import org.apache.syncope.common.rest.api.service.DomainService;
+import org.apache.syncope.common.rest.api.service.DynRealmService;
 import org.apache.syncope.common.rest.api.service.LoggerService;
 import org.apache.syncope.common.rest.api.service.NotificationService;
 import org.apache.syncope.common.rest.api.service.PolicyService;
@@ -182,6 +183,8 @@ public abstract class AbstractITCase {
 
     protected static RoleService roleService;
 
+    protected static DynRealmService dynRealmService;
+
     protected static UserService userService;
 
     protected static UserSelfService userSelfService;
@@ -262,6 +265,7 @@ public abstract class AbstractITCase {
         realmService = adminClient.getService(RealmService.class);
         anyObjectService = adminClient.getService(AnyObjectService.class);
         roleService = adminClient.getService(RoleService.class);
+        dynRealmService = adminClient.getService(DynRealmService.class);
         userService = adminClient.getService(UserService.class);
         userSelfService = adminClient.getService(UserSelfService.class);
         userWorkflowService = adminClient.getService(UserWorkflowService.class);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/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
new file mode 100644
index 0000000..e773716
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.fit.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.patch.AttrPatch;
+import org.apache.syncope.common.lib.patch.GroupPatch;
+import org.apache.syncope.common.lib.patch.StringPatchItem;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.DynRealmTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+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.StandardEntitlement;
+import org.apache.syncope.common.rest.api.beans.AnyQuery;
+import org.apache.syncope.common.rest.api.service.DynRealmService;
+import org.apache.syncope.common.rest.api.service.GroupService;
+import org.apache.syncope.common.rest.api.service.UserService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.Test;
+
+public class DynRealmITCase extends AbstractITCase {
+
+    @Test
+    public void misc() {
+        DynRealmTO dynRealm = null;
+        try {
+            dynRealm = new DynRealmTO();
+            dynRealm.setKey("/name" + getUUIDString());
+            dynRealm.setCond("cool==true");
+
+            // invalid key (starts with /)
+            try {
+                dynRealmService.create(dynRealm);
+                fail();
+            } catch (SyncopeClientException e) {
+                assertEquals(ClientExceptionType.InvalidDynRealm, e.getType());
+            }
+            dynRealm.setKey("name" + getUUIDString());
+
+            Response response = dynRealmService.create(dynRealm);
+            dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
+            assertNotNull(dynRealm);
+
+            PagedResult<UserTO> matching = userService.search(new AnyQuery.Builder().fiql("cool==true").build());
+            assertNotNull(matching);
+            assertNotEquals(0, matching.getSize());
+
+            UserTO user = matching.getResult().get(0);
+
+            assertTrue(user.getDynRealms().contains(dynRealm.getKey()));
+        } finally {
+            if (dynRealm != null) {
+                dynRealmService.delete(dynRealm.getKey());
+            }
+        }
+    }
+
+    @Test
+    public void delegatedAdmin() {
+        DynRealmTO dynRealm = null;
+        RoleTO role = null;
+        try {
+            // 1. create dynamic realm for all users and groups having resource-ldap assigned
+            dynRealm = new DynRealmTO();
+            dynRealm.setKey("LDAPLovers" + getUUIDString());
+            dynRealm.setCond("$resources==resource-ldap");
+
+            Response response = dynRealmService.create(dynRealm);
+            dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
+            assertNotNull(dynRealm);
+
+            // 2. create role for such dynamic realm
+            role = new RoleTO();
+            role.setKey("Administer LDAP" + getUUIDString());
+            role.getEntitlements().add(StandardEntitlement.USER_SEARCH);
+            role.getEntitlements().add(StandardEntitlement.USER_READ);
+            role.getEntitlements().add(StandardEntitlement.USER_UPDATE);
+            role.getEntitlements().add(StandardEntitlement.GROUP_READ);
+            role.getEntitlements().add(StandardEntitlement.GROUP_UPDATE);
+            role.getDynRealms().add(dynRealm.getKey());
+
+            role = createRole(role);
+            assertNotNull(role);
+
+            // 3. assign the new role to vivaldi
+            UserPatch userPatch = new UserPatch();
+            userPatch.setKey("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee");
+            userPatch.getRoles().add(new StringPatchItem.Builder().value(role.getKey()).build());
+            UserTO vivaldi = updateUser(userPatch).getEntity();
+            assertNotNull(vivaldi);
+            assertTrue(vivaldi.getRoles().contains(role.getKey()));
+
+            // 4. create new user and group, assign resource-ldap
+            UserTO user = UserITCase.getUniqueSampleTO("dynRealmUser@apache.org");
+            user.setRealm("/even/two");
+            user.getResources().clear();
+            user.getResources().add(RESOURCE_NAME_LDAP);
+            user = createUser(user).getEntity();
+            assertNotNull(user);
+            final String userKey = user.getKey();
+
+            GroupTO group = GroupITCase.getSampleTO("dynRealmGroup");
+            group.setRealm("/odd");
+            group.getResources().clear();
+            group.getResources().add(RESOURCE_NAME_LDAP);
+            group = createGroup(group).getEntity();
+            assertNotNull(group);
+            final String groupKey = group.getKey();
+
+            // 5. verify that the new user and group are found when searching by dynamic realm
+            PagedResult<UserTO> matchingUsers = userService.search(new AnyQuery.Builder().realm("/").fiql(
+                    SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
+            assertTrue(IterableUtils.matchesAny(matchingUsers.getResult(), new Predicate<UserTO>() {
+
+                @Override
+                public boolean evaluate(final UserTO object) {
+                    return object.getKey().equals(userKey);
+                }
+            }));
+            PagedResult<GroupTO> matchingGroups = groupService.search(new AnyQuery.Builder().realm("/").fiql(
+                    SyncopeClient.getGroupSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
+            assertTrue(IterableUtils.matchesAny(matchingGroups.getResult(), new Predicate<GroupTO>() {
+
+                @Override
+                public boolean evaluate(final GroupTO object) {
+                    return object.getKey().equals(groupKey);
+                }
+            }));
+
+            // 6. perpare to act as vivaldi
+            SyncopeClient vivaldiClient = clientFactory.create("vivaldi", ADMIN_PWD);
+            UserService vivaldiUserService = vivaldiClient.getService(UserService.class);
+            GroupService vivaldiGroupService = vivaldiClient.getService(GroupService.class);
+
+            // 7. verify delegated administration
+            // USER_READ
+            assertNotNull(vivaldiUserService.read(userKey));
+
+            // GROUP_READ
+            assertNotNull(vivaldiGroupService.read(groupKey));
+
+            // USER_SEARCH
+            matchingUsers = vivaldiUserService.search(new AnyQuery.Builder().realm("/").build());
+            assertTrue(IterableUtils.matchesAny(matchingUsers.getResult(), new Predicate<UserTO>() {
+
+                @Override
+                public boolean evaluate(final UserTO object) {
+                    return object.getKey().equals(userKey);
+                }
+            }));
+
+            // USER_UPDATE
+            userPatch = new UserPatch();
+            userPatch.setKey(userKey);
+            userPatch.getResources().add(new StringPatchItem.Builder().value(RESOURCE_NAME_NOPROPAGATION).build());
+            user = vivaldiUserService.update(userPatch).
+                    readEntity(new GenericType<ProvisioningResult<UserTO>>() {
+                    }).getEntity();
+            assertNotNull(user);
+            assertTrue(user.getResources().contains(RESOURCE_NAME_NOPROPAGATION));
+
+            // GROUP_UPDATE
+            GroupPatch groupPatch = new GroupPatch();
+            groupPatch.setKey(groupKey);
+            groupPatch.getPlainAttrs().add(new AttrPatch.Builder().attrTO(attrTO("icon", "modified")).build());
+            group = vivaldiGroupService.update(groupPatch).
+                    readEntity(new GenericType<ProvisioningResult<GroupTO>>() {
+                    }).getEntity();
+            assertNotNull(group);
+            assertEquals("modified", group.getPlainAttrMap().get("icon").getValues().get(0));
+        } finally {
+            if (role != null) {
+                roleService.delete(role.getKey());
+            }
+            if (dynRealm != null) {
+                dynRealmService.delete(dynRealm.getKey());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1bb6723/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java
index 8a8e6a2..9db9031 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ExceptionMapperITCase.java
@@ -125,7 +125,7 @@ public class ExceptionMapperITCase extends AbstractITCase {
             fail();
         } catch (Exception e) {
             String message = ERROR_MESSAGES.getProperty("errMessage.UniqueConstraintViolation");
-            assertEquals(e.getMessage(), "DataIntegrityViolation [" + message + "]");
+            assertEquals("EntityExists [" + message + "]", e.getMessage());
         }
     }