syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ilgro...@apache.org
Subject [2/3] syncope git commit: [SYNCOPE-140] Implemented
Date Fri, 08 May 2015 15:03:07 GMT
http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/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 5264fcd..46b51dc 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
@@ -18,7 +18,10 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import javax.annotation.Resource;
@@ -26,6 +29,7 @@ import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
+import org.apache.commons.collections4.Transformer;
 import org.apache.syncope.common.lib.types.AttributableType;
 import org.apache.syncope.common.lib.types.Entitlement;
 import org.apache.syncope.common.lib.types.SubjectType;
@@ -47,8 +51,14 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.misc.security.AuthContextUtils;
 import org.apache.syncope.core.misc.security.UnauthorizedException;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.entity.Role;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.jpa.entity.JPADynGroupMembership;
+import org.apache.syncope.core.persistence.jpa.entity.JPADynRoleMembership;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 @Repository
@@ -57,6 +67,9 @@ public class JPAUserDAO extends AbstractSubjectDAO<UPlainAttr, UDerAttr, UVirAtt
     @Autowired
     private GroupDAO groupDAO;
 
+    @Autowired
+    private RoleDAO roleDAO;
+
     @Resource(name = "anonymousUser")
     private String anonymousUser;
 
@@ -187,12 +200,15 @@ public class JPAUserDAO extends AbstractSubjectDAO<UPlainAttr, UDerAttr, UVirAtt
 
     @Override
     public User save(final User user) {
-        final User merged = entityManager.merge(user);
+        User merged = entityManager.merge(user);
         for (VirAttr virAttr : merged.getVirAttrs()) {
             virAttr.getValues().clear();
             virAttr.getValues().addAll(user.getVirAttr(virAttr.getSchema().getKey()).getValues());
         }
 
+        roleDAO.refreshDynMemberships(merged);
+        groupDAO.refreshDynMemberships(merged);
+
         return merged;
     }
 
@@ -220,6 +236,13 @@ public class JPAUserDAO extends AbstractSubjectDAO<UPlainAttr, UDerAttr, UVirAtt
         }
         user.getMemberships().clear();
 
+        for (Role role : findDynRoleMemberships(user)) {
+            role.getDynMembership().removeUser(user);
+        }
+        for (Group group : findDynGroupMemberships(user)) {
+            group.getDynMembership().removeUser(user);
+        }
+
         entityManager.remove(user);
     }
 
@@ -277,4 +300,82 @@ public class JPAUserDAO extends AbstractSubjectDAO<UPlainAttr, UDerAttr, UVirAtt
         return user;
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public List<Role> findDynRoleMemberships(final User user) {
+        TypedQuery<Role> query = entityManager.createQuery(
+                "SELECT e.role FROM " + JPADynRoleMembership.class.getSimpleName()
+                + " e WHERE :user MEMBER OF e.users", Role.class);
+        query.setParameter("user", user);
+
+        return query.getResultList();
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public List<Group> findDynGroupMemberships(final User user) {
+        TypedQuery<Group> query = entityManager.createQuery(
+                "SELECT e.group FROM " + JPADynGroupMembership.class.getSimpleName()
+                + " e WHERE :user MEMBER OF e.users", Group.class);
+        query.setParameter("user", user);
+
+        return query.getResultList();
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public Collection<Role> findAllRoles(final User user) {
+        return CollectionUtils.union(user.getRoles(), findDynRoleMemberships(user));
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public Collection<Group> findAllGroups(final User user) {
+        return CollectionUtils.union(
+                CollectionUtils.collect(user.getMemberships(), new Transformer<Membership, Group>() {
+
+                    @Override
+                    public Group transform(final Membership input) {
+                        return input.getGroup();
+                    }
+                }, new ArrayList<Group>()),
+                findDynGroupMemberships(user));
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public Collection<Long> findAllGroupKeys(final User user) {
+        return CollectionUtils.collect(findAllGroups(user), new Transformer<Group, Long>() {
+
+            @Override
+            public Long transform(final Group input) {
+                return input.getKey();
+            }
+        });
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public Collection<ExternalResource> findAllResources(final User user) {
+        Set<ExternalResource> result = new HashSet<>();
+        result.addAll(user.getResources());
+        for (Group group : findAllGroups(user)) {
+            result.addAll(group.getResources());
+        }
+
+        return result;
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public Collection<String> findAllResourceNames(final User user) {
+        return CollectionUtils.collect(findAllResources(user), new Transformer<ExternalResource, String>() {
+
+            @Override
+            public String transform(final ExternalResource input) {
+                return input.getKey();
+            }
+        });
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/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 9e80ff7..11b3298 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
@@ -110,6 +110,18 @@ class SearchSupport {
         return new SearchView("svm", field().name + "_membership");
     }
 
+    public SearchView dyngroupmembership() {
+        return new SearchView("svdg", field().name + "_dyngroupmembership");
+    }
+
+    public SearchView role() {
+        return new SearchView("svr", field().name + "_role");
+    }
+
+    public SearchView dynrolemembership() {
+        return new SearchView("svdr", field().name + "_dynrolemembership");
+    }
+
     public SearchView nullAttr() {
         return new SearchView("svna", field().name + "_null_attr");
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java
new file mode 100644
index 0000000..6e47b34
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java
@@ -0,0 +1,65 @@
+/*
+ * 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 java.util.List;
+import javax.persistence.MappedSuperclass;
+import javax.validation.constraints.NotNull;
+import org.apache.syncope.core.persistence.api.entity.DynMembership;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
+
+@MappedSuperclass
+public abstract class AbstractDynMembership extends AbstractEntity<Long> implements DynMembership {
+
+    private static final long serialVersionUID = 921821654690948787L;
+
+    @NotNull
+    private String fiql;
+
+    @Override
+    public String getFIQLCond() {
+        return fiql;
+    }
+
+    @Override
+    public void setFIQLCond(final String fiql) {
+        this.fiql = fiql;
+    }
+
+    protected abstract List<JPAUser> internalGetUsers();
+
+    @Override
+    public boolean addUser(final User user) {
+        checkType(user, JPAUser.class);
+        return internalGetUsers().add((JPAUser) user);
+    }
+
+    @Override
+    public boolean removeUser(final User user) {
+        checkType(user, JPAUser.class);
+        return internalGetUsers().remove((JPAUser) user);
+    }
+
+    @Override
+    public List<? extends User> getUsers() {
+        return internalGetUsers();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSubject.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSubject.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSubject.java
index b0a7ab4..166309f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSubject.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSubject.java
@@ -52,17 +52,18 @@ public abstract class AbstractSubject<P extends PlainAttr, D extends DerAttr, V
         this.realm = (JPARealm) realm;
     }
 
-    protected abstract Set<? extends ExternalResource> internalGetResources();
+    protected abstract Set<JPAExternalResource> internalGetResources();
 
     @Override
-    @SuppressWarnings("unchecked")
     public boolean addResource(final ExternalResource resource) {
-        return ((Set<ExternalResource>) internalGetResources()).add(resource);
+        checkType(resource, JPAExternalResource.class);
+        return internalGetResources().add((JPAExternalResource) resource);
     }
 
     @Override
     public boolean removeResource(final ExternalResource resource) {
-        return internalGetResources().remove(resource);
+        checkType(resource, JPAExternalResource.class);
+        return internalGetResources().remove((JPAExternalResource) resource);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AnnotatedEntityListener.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AnnotatedEntityListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AnnotatedEntityListener.java
index f814033..8a9547e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AnnotatedEntityListener.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AnnotatedEntityListener.java
@@ -28,9 +28,6 @@ import org.slf4j.LoggerFactory;
 
 public class AnnotatedEntityListener {
 
-    /**
-     * Logger.
-     */
     private static final Logger LOG = LoggerFactory.getLogger(AnnotatedEntityListener.class);
 
     @PrePersist

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynGroupMembership.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynGroupMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynGroupMembership.java
new file mode 100644
index 0000000..1f55348
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynGroupMembership.java
@@ -0,0 +1,77 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import org.apache.syncope.core.persistence.api.entity.DynGroupMembership;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
+
+@Entity
+@Table(name = JPADynGroupMembership.TABLE)
+public class JPADynGroupMembership extends AbstractDynMembership implements DynGroupMembership {
+
+    private static final long serialVersionUID = -7336814163949640354L;
+
+    public static final String TABLE = "DynGroupMembership";
+
+    @Id
+    private Long id;
+
+    @OneToOne
+    private JPAGroup group;
+
+    @ManyToMany
+    @JoinTable(joinColumns =
+            @JoinColumn(name = "dynGroupMembership_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "user_id"))
+    private List<JPAUser> users = new ArrayList<>();
+
+    @Override
+    public Long getKey() {
+        return id;
+    }
+
+    @Override
+    protected List<JPAUser> internalGetUsers() {
+        return users;
+    }
+
+    @Override
+    public Group getGroup() {
+        return group;
+    }
+
+    @Override
+    public void setGroup(final Group role) {
+        checkType(role, JPAGroup.class);
+        this.group = (JPAGroup) role;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRoleMembership.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRoleMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRoleMembership.java
new file mode 100644
index 0000000..4796abf
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRoleMembership.java
@@ -0,0 +1,76 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import org.apache.syncope.core.persistence.api.entity.DynRoleMembership;
+import org.apache.syncope.core.persistence.api.entity.Role;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
+
+@Entity
+@Table(name = JPADynRoleMembership.TABLE)
+public class JPADynRoleMembership extends AbstractDynMembership implements DynRoleMembership {
+
+    private static final long serialVersionUID = -7336814163949640354L;
+
+    public static final String TABLE = "DynRoleMembership";
+
+    @Id
+    private Long id;
+
+    @OneToOne
+    private JPARole role;
+
+    @ManyToMany
+    @JoinTable(joinColumns =
+            @JoinColumn(name = "dynRoleMembership_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "user_id"))
+    private List<JPAUser> users = new ArrayList<>();
+
+    @Override
+    public Long getKey() {
+        return id;
+    }
+
+    @Override
+    protected List<JPAUser> internalGetUsers() {
+        return users;
+    }
+
+    @Override
+    public Role getRole() {
+        return role;
+    }
+
+    @Override
+    public void setRole(final Role role) {
+        checkType(role, JPARole.class);
+        this.role = (JPARole) role;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/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 3002dcc..048955b 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
@@ -21,6 +21,8 @@ package org.apache.syncope.core.persistence.jpa.entity;
 import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
 import org.apache.syncope.core.persistence.api.entity.ConnPoolConf;
+import org.apache.syncope.core.persistence.api.entity.DynGroupMembership;
+import org.apache.syncope.core.persistence.api.entity.DynRoleMembership;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
@@ -265,6 +267,10 @@ public class JPAEntityFactory implements EntityFactory {
             result = (T) new JPASecurityQuestion();
         } else if (reference.equals(Logger.class)) {
             result = (T) new JPALogger();
+        } else if (reference.equals(DynRoleMembership.class)) {
+            result = (T) new JPADynRoleMembership();
+        } else if (reference.equals(DynGroupMembership.class)) {
+            result = (T) new JPADynGroupMembership();
         } else {
             throw new IllegalArgumentException("Could not find a JPA implementation of " + reference.getName());
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/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 f88f430..0360f54 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
@@ -23,6 +23,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import javax.persistence.Cacheable;
+import javax.persistence.CascadeType;
 import javax.persistence.CollectionTable;
 import javax.persistence.Column;
 import javax.persistence.ElementCollection;
@@ -32,9 +33,11 @@ import javax.persistence.Id;
 import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
+import javax.persistence.OneToOne;
 import javax.persistence.Table;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
+import org.apache.syncope.core.persistence.api.entity.DynRoleMembership;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.Role;
 
@@ -69,6 +72,10 @@ public class JPARole extends AbstractEntity<Long> implements Role {
     @Valid
     private List<JPARealm> realms = new ArrayList<>();
 
+    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "role")
+    @Valid
+    private JPADynRoleMembership dynMembership;
+
     @Override
     public Long getKey() {
         return id;
@@ -106,4 +113,14 @@ public class JPARole extends AbstractEntity<Long> implements Role {
         return realms;
     }
 
+    @Override
+    public DynRoleMembership getDynMembership() {
+        return dynMembership;
+    }
+
+    @Override
+    public void setDynMembership(final DynRoleMembership dynMembership) {
+        checkType(dynMembership, JPADynRoleMembership.class);
+        this.dynMembership = (JPADynRoleMembership) dynMembership;
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
index 720b875..20e8808 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
@@ -33,6 +33,7 @@ import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
 import javax.persistence.Table;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
@@ -40,7 +41,7 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.syncope.core.persistence.api.entity.AttrTemplate;
-import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.DynGroupMembership;
 import org.apache.syncope.core.persistence.api.entity.Schema;
 import org.apache.syncope.core.persistence.api.entity.membership.MDerAttrTemplate;
 import org.apache.syncope.core.persistence.api.entity.membership.MPlainAttrTemplate;
@@ -55,6 +56,7 @@ import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.validation.entity.GroupCheck;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractSubject;
+import org.apache.syncope.core.persistence.jpa.entity.JPADynGroupMembership;
 import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource;
 import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMPlainAttrTemplate;
 import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMDerAttrTemplate;
@@ -131,6 +133,10 @@ public class JPAGroup extends AbstractSubject<GPlainAttr, GDerAttr, GVirAttr> im
     @Valid
     private Set<JPAExternalResource> resources;
 
+    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "group")
+    @Valid
+    private JPADynGroupMembership dynMembership;
+
     public JPAGroup() {
         super();
 
@@ -154,7 +160,7 @@ public class JPAGroup extends AbstractSubject<GPlainAttr, GDerAttr, GVirAttr> im
     }
 
     @Override
-    protected Set<? extends ExternalResource> internalGetResources() {
+    protected Set<JPAExternalResource> internalGetResources() {
         return resources;
     }
 
@@ -286,4 +292,16 @@ public class JPAGroup extends AbstractSubject<GPlainAttr, GDerAttr, GVirAttr> im
     public List<? extends GVirAttr> getVirAttrs() {
         return virAttrs;
     }
+
+    @Override
+    public DynGroupMembership getDynMembership() {
+        return dynMembership;
+    }
+
+    @Override
+    public void setDynMembership(final DynGroupMembership dynMembership) {
+        checkType(dynMembership, JPADynGroupMembership.class);
+        this.dynMembership = (JPADynGroupMembership) dynMembership;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
index 3170c14..029c96b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.jpa.entity.user;
 
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
@@ -53,9 +54,7 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
-import org.apache.syncope.core.persistence.api.entity.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.membership.Membership;
-import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
 import org.apache.syncope.core.persistence.api.entity.user.UDerAttr;
 import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
@@ -71,9 +70,6 @@ import org.apache.syncope.core.misc.security.SecureRandomUtils;
 import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.jpa.entity.JPARole;
 
-/**
- * JPA user bean.
- */
 @Entity
 @Table(name = JPAUser.TABLE)
 @Cacheable
@@ -206,7 +202,7 @@ public class JPAUser extends AbstractSubject<UPlainAttr, UDerAttr, UVirAttr> imp
     }
 
     @Override
-    protected Set<? extends ExternalResource> internalGetResources() {
+    protected Set<JPAExternalResource> internalGetResources() {
         return resources;
     }
 
@@ -256,44 +252,17 @@ public class JPAUser extends AbstractSubject<UPlainAttr, UDerAttr, UVirAttr> imp
     }
 
     @Override
-    public List<Group> getGroups() {
-        return CollectionUtils.collect(memberships, new Transformer<Membership, Group>() {
-
-            @Override
-            public Group transform(final Membership input) {
-                return input.getGroup();
-            }
-        }, new ArrayList<Group>());
-    }
-
-    @Override
-    public Set<Long> getGroupKeys() {
-        return CollectionUtils.collect(getGroups(), new Transformer<Group, Long>() {
+    public Collection<Long> getStaticGroupKeys() {
+        return CollectionUtils.collect(memberships, new Transformer<Membership, Long>() {
 
             @Override
-            public Long transform(final Group input) {
-                return input.getKey();
+            public Long transform(final Membership membership) {
+                return membership.getGroup().getKey();
             }
         }, new HashSet<Long>());
     }
 
     @Override
-    public Set<ExternalResource> getResources() {
-        Set<ExternalResource> result = new HashSet<>();
-        result.addAll(super.getResources());
-        for (Group group : getGroups()) {
-            result.addAll(group.getResources());
-        }
-
-        return result;
-    }
-
-    @Override
-    public Set<? extends ExternalResource> getOwnResources() {
-        return super.getResources();
-    }
-
-    @Override
     public String getPassword() {
         return password;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserValidator.java
index fac60cb..19fc7cf 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserValidator.java
@@ -25,7 +25,6 @@ import javax.validation.ConstraintValidatorContext;
 import org.apache.syncope.common.lib.types.AccountPolicySpec;
 import org.apache.syncope.common.lib.types.EntityViolationType;
 import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
@@ -36,6 +35,7 @@ import org.apache.syncope.core.misc.policy.AccountPolicyException;
 import org.apache.syncope.core.misc.policy.PasswordPolicyEnforcer;
 import org.apache.syncope.core.misc.policy.PolicyEvaluator;
 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.entity.Realm;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -48,7 +48,7 @@ public class UserValidator extends AbstractValidator<UserCheck, User> {
     private String anonymousUser;
 
     @Autowired
-    private PolicyDAO policyDAO;
+    private UserDAO userDAO;
 
     @Autowired
     private RealmDAO realmDAO;
@@ -155,7 +155,7 @@ public class UserValidator extends AbstractValidator<UserCheck, User> {
         PasswordPolicy policy;
 
         // add resource policies
-        for (ExternalResource resource : user.getResources()) {
+        for (ExternalResource resource : userDAO.findAllResources(user)) {
             policy = resource.getPasswordPolicy();
             if (policy != null) {
                 policies.add(policy);
@@ -179,7 +179,7 @@ public class UserValidator extends AbstractValidator<UserCheck, User> {
         AccountPolicy policy;
 
         // add resource policies
-        for (ExternalResource resource : user.getResources()) {
+        for (ExternalResource resource : userDAO.findAllResources(user)) {
             policy = resource.getAccountPolicy();
             if (policy != null) {
                 policies.add(policy);

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
index 3a61895..8c58d33 100644
--- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
+++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
@@ -57,6 +57,15 @@ under the License.
     </attributes>
   </entity>
 
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.JPADynRoleMembership">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_DynRoleMembership" strategy="TABLE"/>
+        <table-generator name="SEQ_DynRoleMembership" pk-column-value="SEQ_DynRoleMembership" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+
   <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser">
     <attributes>
       <id name="id">
@@ -75,6 +84,15 @@ under the License.
     </attributes>
   </entity>
 
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.JPADynGroupMembership">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_DynGroupMembership" strategy="TABLE"/>
+        <table-generator name="SEQ_DynGroupMembership" pk-column-value="SEQ_DynGroupMembership" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+  
   <entity class="org.apache.syncope.core.persistence.jpa.entity.membership.JPAMembership">
     <attributes>
       <id name="id">

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
index 97af974..bd7f664 100644
--- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
+++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
@@ -57,6 +57,15 @@ under the License.
     </attributes>
   </entity>
   
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.JPADynRoleMembership">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_DynRoleMembership" strategy="TABLE"/>
+        <table-generator name="SEQ_DynRoleMembership" pk-column-value="SEQ_DynRoleMembership" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+  
   <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser">
     <attributes>
       <id name="id">
@@ -75,6 +84,15 @@ under the License.
     </attributes>
   </entity>
 
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.JPADynGroupMembership">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_DynGroupMembership" strategy="TABLE"/>
+        <table-generator name="SEQ_DynGroupMembership" pk-column-value="SEQ_DynGroupMembership" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+  
   <entity class="org.apache.syncope.core.persistence.jpa.entity.membership.JPAMembership">
     <attributes>
       <id name="id">

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
index 5921b94..5562674 100644
--- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
+++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
@@ -56,7 +56,16 @@ under the License.
       </id>
     </attributes>
   </entity>
-
+  
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.JPADynRoleMembership">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_DynRoleMembership" strategy="TABLE"/>
+        <table-generator name="SEQ_DynRoleMembership" pk-column-value="SEQ_DynRoleMembership" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+  
   <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser">
     <attributes>
       <id name="id">
@@ -74,7 +83,16 @@ under the License.
       </id>
     </attributes>
   </entity>
-
+  
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.JPADynGroupMembership">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_DynGroupMembership" strategy="TABLE"/>
+        <table-generator name="SEQ_DynGroupMembership" pk-column-value="SEQ_DynGroupMembership" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+  
   <entity class="org.apache.syncope.core.persistence.jpa.entity.membership.JPAMembership">
     <attributes>
       <id name="id">

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/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 8cd392d..f7bce20 100644
--- a/core/persistence-jpa/src/main/resources/views.xml
+++ b/core/persistence-jpa/src/main/resources/views.xml
@@ -68,9 +68,29 @@ under the License.
   <entry key="user_search_membership">
     CREATE VIEW user_search_membership AS
 
-    SELECT m.user_id AS subject_id, r.id AS group_id, r.name AS group_name
-    FROM Membership m, SyncopeGroup r
-    WHERE m.group_id = r.id
+    SELECT m.user_id AS subject_id, g.id AS group_id, g.name AS group_name
+    FROM Membership m, SyncopeGroup g
+    WHERE m.group_id = g.id
+  </entry>
+  <entry key="user_search_dyngroupmembership">
+    CREATE VIEW user_search_dyngroupmembership AS
+
+    SELECT ds.user_id AS subject_id, d.group_id AS group_id
+    FROM DynGroupMembership d, DynGroupMembership_SyncopeUser ds
+    WHERE d.id = ds.dynGroupMembership_id
+  </entry>
+  <entry key="user_search_role">
+    CREATE VIEW user_search_role AS
+
+    SELECT ss.user_id AS subject_id, ss.role_id AS role_id
+    FROM SyncopeUser_SyncopeRole ss
+  </entry>
+  <entry key="user_search_dynrolemembership">
+    CREATE VIEW user_search_dynrolemembership AS
+
+    SELECT ds.user_id AS subject_id, d.role_id AS role_id
+    FROM DynRoleMembership d, DynRoleMembership_SyncopeUser ds
+    WHERE d.id = ds.dynRoleMembership_id
   </entry>
   <entry key="user_search_resource">
     CREATE VIEW user_search_resource AS

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/AttrTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/AttrTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/AttrTest.java
index 289f898..4ad4dd0 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/AttrTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/AttrTest.java
@@ -56,7 +56,7 @@ public class AttrTest extends AbstractTest {
     private PlainAttrDAO plainAttrDAO;
 
     @Autowired
-    private PlainSchemaDAO userSchemaDAO;
+    private PlainSchemaDAO plainSchemaDAO;
 
     @Test
     public void findById() {
@@ -78,7 +78,7 @@ public class AttrTest extends AbstractTest {
     public void save() throws ClassNotFoundException {
         User user = userDAO.find(1L);
 
-        UPlainSchema emailSchema = userSchemaDAO.find("email", UPlainSchema.class);
+        UPlainSchema emailSchema = plainSchemaDAO.find("email", UPlainSchema.class);
         assertNotNull(emailSchema);
 
         UPlainAttr attribute = entityFactory.newEntity(UPlainAttr.class);
@@ -106,7 +106,7 @@ public class AttrTest extends AbstractTest {
     public void saveWithEnum() throws ClassNotFoundException {
         User user = userDAO.find(1L);
 
-        UPlainSchema gender = userSchemaDAO.find("gender", UPlainSchema.class);
+        UPlainSchema gender = plainSchemaDAO.find("gender", UPlainSchema.class);
         assertNotNull(gender);
         assertNotNull(gender.getType());
         assertNotNull(gender.getEnumerationValues());
@@ -140,10 +140,10 @@ public class AttrTest extends AbstractTest {
     public void validateAndSave() {
         User user = userDAO.find(1L);
 
-        final UPlainSchema emailSchema = userSchemaDAO.find("email", UPlainSchema.class);
+        final UPlainSchema emailSchema = plainSchemaDAO.find("email", UPlainSchema.class);
         assertNotNull(emailSchema);
 
-        final UPlainSchema fullnameSchema = userSchemaDAO.find("fullname", UPlainSchema.class);
+        final UPlainSchema fullnameSchema = plainSchemaDAO.find("fullname", UPlainSchema.class);
         assertNotNull(fullnameSchema);
 
         UPlainAttr attribute = entityFactory.newEntity(UPlainAttr.class);
@@ -176,7 +176,7 @@ public class AttrTest extends AbstractTest {
     public void saveWithEncrypted() throws Exception {
         User user = userDAO.find(1L);
 
-        final UPlainSchema obscureSchema = userSchemaDAO.find("obscure", UPlainSchema.class);
+        final UPlainSchema obscureSchema = plainSchemaDAO.find("obscure", UPlainSchema.class);
         assertNotNull(obscureSchema);
         assertNotNull(obscureSchema.getSecretKey());
         assertNotNull(obscureSchema.getCipherAlgorithm());
@@ -200,7 +200,7 @@ public class AttrTest extends AbstractTest {
     public void saveWithBinary() throws UnsupportedEncodingException {
         User user = userDAO.find(1L);
 
-        final UPlainSchema photoSchema = userSchemaDAO.find("photo", UPlainSchema.class);
+        final UPlainSchema photoSchema = plainSchemaDAO.find("photo", UPlainSchema.class);
         assertNotNull(photoSchema);
         assertNotNull(photoSchema.getMimeType());
 
@@ -229,7 +229,7 @@ public class AttrTest extends AbstractTest {
 
         plainAttrDAO.delete(attribute.getKey(), UPlainAttr.class);
 
-        UPlainSchema schema = userSchemaDAO.find(attrSchemaName, UPlainSchema.class);
+        UPlainSchema schema = plainSchemaDAO.find(attrSchemaName, UPlainSchema.class);
         assertNotNull("user attribute schema deleted when deleting values", schema);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/RoleTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/RoleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/RoleTest.java
index a7bf883..7a20e89 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/RoleTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/RoleTest.java
@@ -28,7 +28,6 @@ import java.util.List;
 import org.apache.syncope.common.lib.types.Entitlement;
 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.SubjectSearchDAO;
 import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.Test;
@@ -42,9 +41,6 @@ public class RoleTest extends AbstractTest {
     private RoleDAO roleDAO;
 
     @Autowired
-    private SubjectSearchDAO searchDAO;
-
-    @Autowired
     private RealmDAO realmDAO;
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/SubjectSearchTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/SubjectSearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/SubjectSearchTest.java
index d0a6d0c..22e7494 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/SubjectSearchTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/entity/SubjectSearchTest.java
@@ -36,9 +36,10 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
-import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
+import org.apache.syncope.core.persistence.api.dao.search.GroupCond;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.dao.search.ResourceCond;
+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.dao.search.SubjectCond;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -65,14 +66,16 @@ public class SubjectSearchTest extends AbstractTest {
         User user = userDAO.find(1L);
         assertNotNull(user);
 
-        MembershipCond membershipCond = new MembershipCond();
-        membershipCond.setGroupId(5L);
+        GroupCond groupCond = new GroupCond();
+        groupCond.setGroupKey(5L);
+        assertFalse(searchDAO.matches(user, SearchCond.getLeafCond(groupCond), SubjectType.USER));
 
-        assertFalse(searchDAO.matches(user, SearchCond.getLeafCond(membershipCond), SubjectType.USER));
+        groupCond.setGroupKey(1L);
+        assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(groupCond), SubjectType.USER));
 
-        membershipCond.setGroupId(1L);
-
-        assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(membershipCond), SubjectType.USER));
+        RoleCond roleCond = new RoleCond();
+        roleCond.setRoleKey(3L);
+        assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(roleCond), SubjectType.USER));
     }
 
     @Test
@@ -93,15 +96,15 @@ public class SubjectSearchTest extends AbstractTest {
         fullnameLeafCond.setSchema("fullname");
         fullnameLeafCond.setExpression("%o%");
 
-        MembershipCond membershipCond = new MembershipCond();
-        membershipCond.setGroupId(1L);
+        GroupCond groupCond = new GroupCond();
+        groupCond.setGroupKey(1L);
 
         AttributeCond loginDateCond = new AttributeCond(AttributeCond.Type.EQ);
         loginDateCond.setSchema("loginDate");
         loginDateCond.setExpression("2009-05-26");
 
         SearchCond subCond = SearchCond.getAndCond(SearchCond.getLeafCond(fullnameLeafCond), SearchCond.getLeafCond(
-                membershipCond));
+                groupCond));
 
         assertTrue(subCond.isValid());
 
@@ -157,15 +160,15 @@ public class SubjectSearchTest extends AbstractTest {
         fullnameLeafCond.setSchema("fullname");
         fullnameLeafCond.setExpression("%o%");
 
-        MembershipCond membershipCond = new MembershipCond();
-        membershipCond.setGroupId(1L);
+        GroupCond groupCond = new GroupCond();
+        groupCond.setGroupKey(1L);
 
         AttributeCond loginDateCond = new AttributeCond(AttributeCond.Type.EQ);
         loginDateCond.setSchema("loginDate");
         loginDateCond.setExpression("2009-05-26");
 
         SearchCond subCond = SearchCond.getAndCond(
-                SearchCond.getLeafCond(fullnameLeafCond), SearchCond.getLeafCond(membershipCond));
+                SearchCond.getLeafCond(fullnameLeafCond), SearchCond.getLeafCond(groupCond));
 
         assertTrue(subCond.isValid());
 
@@ -187,25 +190,36 @@ public class SubjectSearchTest extends AbstractTest {
     }
 
     @Test
-    public void searchByMembership() {
-        MembershipCond membershipCond = new MembershipCond();
-        membershipCond.setGroupId(1L);
+    public void searchByGroup() {
+        GroupCond groupCond = new GroupCond();
+        groupCond.setGroupKey(1L);
 
         List<User> users = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS,
-                SearchCond.getLeafCond(membershipCond), SubjectType.USER);
+                SearchCond.getLeafCond(groupCond), SubjectType.USER);
         assertNotNull(users);
         assertEquals(2, users.size());
 
-        membershipCond = new MembershipCond();
-        membershipCond.setGroupId(5L);
+        groupCond = new GroupCond();
+        groupCond.setGroupKey(5L);
 
         users = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS,
-                SearchCond.getNotLeafCond(membershipCond), SubjectType.USER);
+                SearchCond.getNotLeafCond(groupCond), SubjectType.USER);
         assertNotNull(users);
         assertEquals(5, users.size());
     }
 
     @Test
+    public void searchByRole() {
+        RoleCond roleCond = new RoleCond();
+        roleCond.setRoleKey(3L);
+
+        List<User> users = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS,
+                SearchCond.getLeafCond(roleCond), SubjectType.USER);
+        assertNotNull(users);
+        assertEquals(1, users.size());
+    }
+
+    @Test
     public void searchByIsNull() {
         AttributeCond coolLeafCond = new AttributeCond(AttributeCond.Type.ISNULL);
         coolLeafCond.setSchema("cool");
@@ -233,11 +247,9 @@ public class SubjectSearchTest extends AbstractTest {
         ws1.setResourceName("ws-target-resource-list-mappings-2");
 
         SearchCond searchCondition = SearchCond.getAndCond(SearchCond.getNotLeafCond(ws2), SearchCond.getLeafCond(ws1));
-
         assertTrue(searchCondition.isValid());
 
         List<User> users = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS, searchCondition, SubjectType.USER);
-
         assertNotNull(users);
         assertEquals(1, users.size());
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/GroupTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/GroupTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/GroupTest.java
index 251296a..d6d99ce 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/GroupTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/GroupTest.java
@@ -24,19 +24,32 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.common.lib.types.AttributableType;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 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.entity.DynGroupMembership;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainSchema;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainSchema;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.apache.syncope.core.persistence.jpa.entity.JPADynGroupMembership;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
@@ -45,12 +58,18 @@ import org.springframework.transaction.annotation.Transactional;
 public class GroupTest extends AbstractTest {
 
     @Autowired
+    private EntityManager entityManager;
+
+    @Autowired
     private UserDAO userDAO;
 
     @Autowired
     private GroupDAO groupDAO;
 
     @Autowired
+    private RealmDAO realmDAO;
+
+    @Autowired
     private PlainSchemaDAO plainSchemaDAO;
 
     @Autowired
@@ -68,6 +87,7 @@ public class GroupTest extends AbstractTest {
         assertNotNull("did not find expected user", user);
 
         Group group = entityFactory.newEntity(Group.class);
+        group.setRealm(realmDAO.getRoot());
         group.setName("error");
         group.setUserOwner(user);
         group.setGroupOwner(root);
@@ -98,9 +118,103 @@ public class GroupTest extends AbstractTest {
         groupDAO.flush();
 
         assertNull(groupDAO.find(2L));
-        assertEquals(userDAO.find(2L).getGroups().size(), 2);
+        assertEquals(userDAO.findAllGroups(userDAO.find(2L)).size(), 2);
         assertNull(plainAttrDAO.find(700L, GPlainAttr.class));
         assertNull(plainAttrValueDAO.find(41L, GPlainAttrValue.class));
         assertNotNull(plainSchemaDAO.find("icon", GPlainSchema.class));
     }
+
+    /**
+     * Static copy of {@link org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO} method with same signature:
+     * required for avoiding creating of a new transaction - good for general use case but bad for the way how
+     * this test class is architected.
+     */
+    private Collection<Group> findDynGroupMemberships(final User user) {
+        TypedQuery<Group> query = entityManager.createQuery(
+                "SELECT e.group FROM " + JPADynGroupMembership.class.getSimpleName()
+                + " e WHERE :user MEMBER OF e.users", Group.class);
+        query.setParameter("user", user);
+
+        return query.getResultList();
+    }
+
+    @Test
+    public void dynMembership() {
+        // 0. create user matching the condition below
+        User user = entityFactory.newEntity(User.class);
+        user.setUsername("username");
+        user.setRealm(realmDAO.find("/even/two"));
+
+        UPlainAttr attribute = entityFactory.newEntity(UPlainAttr.class);
+        attribute.setSchema(plainSchemaDAO.find("cool", UPlainSchema.class));
+        attribute.setOwner(user);
+        attribute.addValue("true", attrUtilsFactory.getInstance(AttributableType.USER));
+        user.addPlainAttr(attribute);
+
+        user = userDAO.save(user);
+        Long newUserKey = user.getKey();
+        assertNotNull(newUserKey);
+
+        // 1. create group with dynamic membership
+        Group group = entityFactory.newEntity(Group.class);
+        group.setRealm(realmDAO.getRoot());
+        group.setName("new");
+
+        DynGroupMembership dynMembership = entityFactory.newEntity(DynGroupMembership.class);
+        dynMembership.setFIQLCond("cool==true");
+        dynMembership.setGroup(group);
+
+        group.setDynMembership(dynMembership);
+
+        Group actual = groupDAO.save(group);
+        assertNotNull(actual);
+
+        groupDAO.flush();
+
+        // 2. verify that dynamic membership is there
+        actual = groupDAO.find(actual.getKey());
+        assertNotNull(actual);
+        assertNotNull(actual.getDynMembership());
+        assertNotNull(actual.getDynMembership().getKey());
+        assertEquals(actual, actual.getDynMembership().getGroup());
+
+        // 3. verify that expected users have the created group dynamically assigned
+        assertEquals(2, actual.getDynMembership().getUsers().size());
+        assertEquals(new HashSet<>(Arrays.asList(4L, newUserKey)),
+                CollectionUtils.collect(actual.getDynMembership().getUsers(), new Transformer<User, Long>() {
+
+                    @Override
+                    public Long transform(final User input) {
+                        return input.getKey();
+                    }
+                }, new HashSet<Long>()));
+
+        user = userDAO.find(4L);
+        assertNotNull(user);
+        Collection<Group> dynGroupMemberships = findDynGroupMemberships(user);
+        assertEquals(1, dynGroupMemberships.size());
+        assertTrue(dynGroupMemberships.contains(actual.getDynMembership().getGroup()));
+
+        // 4. delete the new user and verify that dynamic membership was updated
+        userDAO.delete(newUserKey);
+
+        userDAO.flush();
+
+        actual = groupDAO.find(actual.getKey());
+        assertEquals(1, actual.getDynMembership().getUsers().size());
+        assertEquals(4L, actual.getDynMembership().getUsers().get(0).getKey(), 0);
+
+        // 5. delete group and verify that dynamic membership was also removed
+        Long dynMembershipKey = actual.getDynMembership().getKey();
+
+        groupDAO.delete(actual);
+
+        groupDAO.flush();
+
+        assertNull(entityManager.find(JPADynGroupMembership.class, dynMembershipKey));
+
+        dynGroupMemberships = findDynGroupMemberships(user);
+        assertTrue(dynGroupMemberships.isEmpty());
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/ResourceTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/ResourceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/ResourceTest.java
index d9e1864..868f104 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/ResourceTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/ResourceTest.java
@@ -225,7 +225,7 @@ public class ResourceTest extends AbstractTest {
         for (Long id : userIds) {
             User actualUser = userDAO.find(id);
             assertNotNull(actualUser);
-            for (ExternalResource res : actualUser.getResources()) {
+            for (ExternalResource res : userDAO.findAllResources(actualUser)) {
                 assertFalse(res.getKey().equalsIgnoreCase(resource.getKey()));
             }
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/RoleTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/RoleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/RoleTest.java
new file mode 100644
index 0000000..52e3451
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/RoleTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.relationship;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.Entitlement;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+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.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.DynRoleMembership;
+import org.apache.syncope.core.persistence.api.entity.Role;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainSchema;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.apache.syncope.core.persistence.jpa.entity.JPADynRoleMembership;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional
+public class RoleTest extends AbstractTest {
+
+    @Autowired
+    private EntityManager entityManager;
+
+    @Autowired
+    private RoleDAO roleDAO;
+
+    @Autowired
+    private RealmDAO realmDAO;
+
+    @Autowired
+    private PlainSchemaDAO plainSchemaDAO;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    /**
+     * Static copy of {@link org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO} method with same signature:
+     * required for avoiding creating new transaction - good for general use case but bad for the way how
+     * this test class is architected.
+     */
+    private Collection<Role> findDynRoleMemberships(final User user) {
+        TypedQuery<Role> query = entityManager.createQuery(
+                "SELECT e.role FROM " + JPADynRoleMembership.class.getSimpleName()
+                + " e WHERE :user MEMBER OF e.users", Role.class);
+        query.setParameter("user", user);
+
+        return query.getResultList();
+    }
+
+    @Test
+    public void dynMembership() {
+        // 0. create user matching the condition below
+        User user = entityFactory.newEntity(User.class);
+        user.setUsername("username");
+        user.setRealm(realmDAO.find("/even/two"));
+
+        UPlainAttr attribute = entityFactory.newEntity(UPlainAttr.class);
+        attribute.setSchema(plainSchemaDAO.find("cool", UPlainSchema.class));
+        attribute.setOwner(user);
+        attribute.addValue("true", attrUtilsFactory.getInstance(AttributableType.USER));
+        user.addPlainAttr(attribute);
+
+        user = userDAO.save(user);
+        Long newUserKey = user.getKey();
+        assertNotNull(newUserKey);
+
+        // 1. create role with dynamic membership
+        Role role = entityFactory.newEntity(Role.class);
+        role.setName("new");
+        role.addRealm(realmDAO.getRoot());
+        role.addRealm(realmDAO.find("/even/two"));
+        role.getEntitlements().add(Entitlement.LOG_LIST);
+        role.getEntitlements().add(Entitlement.LOG_SET_LEVEL);
+
+        DynRoleMembership dynMembership = entityFactory.newEntity(DynRoleMembership.class);
+        dynMembership.setFIQLCond("cool==true");
+        dynMembership.setRole(role);
+
+        role.setDynMembership(dynMembership);
+
+        Role actual = roleDAO.save(role);
+        assertNotNull(actual);
+
+        roleDAO.flush();
+
+        // 2. verify that dynamic membership is there
+        actual = roleDAO.find(actual.getKey());
+        assertNotNull(actual);
+        assertNotNull(actual.getDynMembership());
+        assertNotNull(actual.getDynMembership().getKey());
+        assertEquals(actual, actual.getDynMembership().getRole());
+
+        // 3. verify that expected users have the created role dynamically assigned
+        assertEquals(2, actual.getDynMembership().getUsers().size());
+        assertEquals(new HashSet<>(Arrays.asList(4L, newUserKey)),
+                CollectionUtils.collect(actual.getDynMembership().getUsers(), new Transformer<User, Long>() {
+
+                    @Override
+                    public Long transform(final User input) {
+                        return input.getKey();
+                    }
+                }, new HashSet<Long>()));
+
+        user = userDAO.find(4L);
+        assertNotNull(user);
+        Collection<Role> dynRoleMemberships = findDynRoleMemberships(user);
+        assertEquals(1, dynRoleMemberships.size());
+        assertTrue(dynRoleMemberships.contains(actual.getDynMembership().getRole()));
+
+        // 4. delete the new user and verify that dynamic membership was updated
+        userDAO.delete(newUserKey);
+
+        userDAO.flush();
+
+        actual = roleDAO.find(actual.getKey());
+        assertEquals(1, actual.getDynMembership().getUsers().size());
+        assertEquals(4L, actual.getDynMembership().getUsers().get(0).getKey(), 0);
+
+        // 5. delete role and verify that dynamic membership was also removed
+        Long dynMembershipKey = actual.getDynMembership().getKey();
+
+        roleDAO.delete(actual);
+
+        roleDAO.flush();
+
+        assertNull(entityManager.find(JPADynRoleMembership.class, dynMembershipKey));
+
+        dynRoleMemberships = findDynRoleMemberships(user);
+        assertTrue(dynRoleMemberships.isEmpty());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/SubjectSearchTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/SubjectSearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/SubjectSearchTest.java
index d3eb0be..025d7cc 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/SubjectSearchTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/relationship/SubjectSearchTest.java
@@ -26,11 +26,17 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.Entitlement;
 import org.apache.syncope.common.lib.types.SubjectType;
 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.RoleDAO;
 import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+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.DynRoleMembership;
+import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
@@ -47,6 +53,12 @@ public class SubjectSearchTest extends AbstractTest {
     @Autowired
     private SubjectSearchDAO searchDAO;
 
+    @Autowired
+    private RealmDAO realmDAO;
+
+    @Autowired
+    private RoleDAO roleDAO;
+
     @Test
     public void issueSYNCOPE95() {
         Set<Group> groups = new HashSet<>(groupDAO.findAll(SyncopeConstants.FULL_ADMIN_REALMS, 1, 100));
@@ -55,18 +67,49 @@ public class SubjectSearchTest extends AbstractTest {
         }
         groupDAO.flush();
 
-        final AttributeCond coolLeafCond = new AttributeCond(AttributeCond.Type.EQ);
+        AttributeCond coolLeafCond = new AttributeCond(AttributeCond.Type.EQ);
         coolLeafCond.setSchema("cool");
         coolLeafCond.setExpression("true");
 
-        final SearchCond cond = SearchCond.getLeafCond(coolLeafCond);
+        SearchCond cond = SearchCond.getLeafCond(coolLeafCond);
         assertTrue(cond.isValid());
 
-        final List<User> users =
-                searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS, cond, SubjectType.USER);
+        List<User> users = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS, cond, SubjectType.USER);
         assertNotNull(users);
         assertEquals(1, users.size());
 
-        assertEquals(Long.valueOf(4L), users.get(0).getKey());
+        assertEquals(4L, users.get(0).getKey(), 0);
+    }
+
+    @Test
+    public void searchByDynMembership() {
+        // 1. create role with dynamic membership
+        Role role = entityFactory.newEntity(Role.class);
+        role.setName("new");
+        role.addRealm(realmDAO.getRoot());
+        role.addRealm(realmDAO.find("/even/two"));
+        role.getEntitlements().add(Entitlement.LOG_LIST);
+        role.getEntitlements().add(Entitlement.LOG_SET_LEVEL);
+
+        DynRoleMembership dynMembership = entityFactory.newEntity(DynRoleMembership.class);
+        dynMembership.setFIQLCond("cool==true");
+        dynMembership.setRole(role);
+
+        role.setDynMembership(dynMembership);
+
+        role = roleDAO.save(role);
+        assertNotNull(role);
+
+        roleDAO.flush();
+
+        // 2. search user by this dynamic role
+        RoleCond roleCond = new RoleCond();
+        roleCond.setRoleKey(role.getKey());
+
+        List<User> users = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS,
+                SearchCond.getLeafCond(roleCond), SubjectType.USER);
+        assertNotNull(users);
+        assertEquals(1, users.size());
+        assertEquals(4L, users.get(0).getKey(), 0);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
index ce28830..c1eff8c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
@@ -230,7 +230,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
 
     protected List<PropagationStatus> propagateStatus(final User user, final StatusMod statusMod) {
         Collection<String> noPropResourceNames =
-                CollectionUtils.removeAll(user.getResourceNames(), statusMod.getResourceNames());
+                CollectionUtils.removeAll(userDAO.findAllResourceNames(user), statusMod.getResourceNames());
 
         List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
                 user, statusMod.getType() != StatusMod.ModType.SUSPEND, noPropResourceNames);
@@ -269,11 +269,11 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
     public List<PropagationStatus> deprovision(final Long userKey, final Collection<String> resources) {
         final User user = userDAO.authFetch(userKey);
 
-        Collection<String> noPropResourceNames = CollectionUtils.removeAll(user.getResourceNames(), resources);
-
-        final List<PropagationTask> tasks =
-                propagationManager.getUserDeleteTasks(userKey, new HashSet<>(resources), noPropResourceNames);
-        final PropagationReporter propagationReporter =
+        List<PropagationTask> tasks = propagationManager.getUserDeleteTasks(
+                userKey,
+                new HashSet<>(resources),
+                CollectionUtils.removeAll(userDAO.findAllResourceNames(user), resources));
+        PropagationReporter propagationReporter =
                 ApplicationContextProvider.getApplicationContext().getBean(PropagationReporter.class);
         try {
             taskExecutor.execute(tasks, propagationReporter);

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java
index 6354041..14c00c7 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java
@@ -51,6 +51,7 @@ import org.apache.syncope.core.persistence.api.entity.group.GVirAttrTemplate;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.user.UVirAttr;
 import org.apache.syncope.core.persistence.api.entity.user.UVirSchema;
+import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -132,11 +133,13 @@ public class VirAttrHandler {
 
         PropagationByResource propByRes = new PropagationByResource();
 
-        final Set<ExternalResource> externalResources = new HashSet<>();
-        if (attributable instanceof Subject) {
-            externalResources.addAll(((Subject<?, ?, ?>) attributable).getResources());
+        Set<ExternalResource> externalResources = new HashSet<>();
+        if (attributable instanceof User) {
+            externalResources.addAll(userDAO.findAllResources((User) attributable));
+        } else if (attributable instanceof Group) {
+            externalResources.addAll(((Group) attributable).getResources());
         } else if (attributable instanceof Membership) {
-            externalResources.addAll(((Membership) attributable).getUser().getResources());
+            externalResources.addAll(userDAO.findAllResources(((Membership) attributable).getUser()));
             externalResources.addAll(((Membership) attributable).getGroup().getResources());
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java
index 6264379..b94ffa2 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java
@@ -91,6 +91,7 @@ import org.apache.syncope.core.misc.jexl.JexlUtils;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -240,7 +241,7 @@ abstract class AbstractAttributableDataBinder {
         Collection<MappingItem> mappings = MappingUtils.getMatchingMappingItems(
                 attrUtils.getMappingItems(resource, MappingPurpose.PROPAGATION), intAttrName, intMappingType);
         for (Iterator<MappingItem> itor = mappings.iterator(); itor.hasNext() && !result;) {
-            final MappingItem mapping = itor.next();
+            MappingItem mapping = itor.next();
             result |= JexlUtils.evaluateMandatoryCondition(mapping.getMandatoryCondition(), attributable);
         }
 
@@ -252,15 +253,17 @@ abstract class AbstractAttributableDataBinder {
 
         boolean result = false;
 
-        if (attributable instanceof Subject) {
-            for (Iterator<? extends ExternalResource> itor =
-                    ((Subject<?, ?, ?>) attributable).getResources().iterator(); itor.hasNext() && !result;) {
-
-                final ExternalResource resource = itor.next();
-                if (resource.isEnforceMandatoryCondition()) {
-                    result |= evaluateMandatoryCondition(
-                            attrUtils, resource, attributable, intAttrName, intMappingType);
-                }
+        Iterable<? extends ExternalResource> iterable = attributable instanceof User
+                ? userDAO.findAllResources((User) attributable)
+                : attributable instanceof Group
+                        ? ((Group) attributable).getResources()
+                        : Collections.<ExternalResource>emptySet();
+
+        for (Iterator<? extends ExternalResource> itor = iterable.iterator(); itor.hasNext() && !result;) {
+            ExternalResource resource = itor.next();
+            if (resource.isEnforceMandatoryCondition()) {
+                result |= evaluateMandatoryCondition(
+                        attrUtils, resource, attributable, intAttrName, intMappingType);
             }
         }
 
@@ -427,11 +430,13 @@ abstract class AbstractAttributableDataBinder {
             LOG.debug("Resources to be added:\n{}", propByRes);
         }
 
-        final Set<ExternalResource> externalResources = new HashSet<>();
-        if (attributable instanceof Subject) {
-            externalResources.addAll(((Subject<?, ?, ?>) attributable).getResources());
+        Set<ExternalResource> externalResources = new HashSet<>();
+        if (attributable instanceof User) {
+            externalResources.addAll(userDAO.findAllResources((User) attributable));
+        } else if (attributable instanceof Group) {
+            externalResources.addAll(((Group) attributable).getResources());
         } else if (attributable instanceof Membership) {
-            externalResources.addAll(((Membership) attributable).getUser().getResources());
+            externalResources.addAll(userDAO.findAllResources(((Membership) attributable).getUser()));
             externalResources.addAll(((Membership) attributable).getGroup().getResources());
         }
 
@@ -777,7 +782,10 @@ abstract class AbstractAttributableDataBinder {
     protected Map<String, String> getAccountIds(final Subject<?, ?, ?> subject, final AttributableType type) {
         Map<String, String> accountIds = new HashMap<>();
 
-        for (ExternalResource resource : subject.getResources()) {
+        Iterable<? extends ExternalResource> iterable = subject instanceof User
+                ? userDAO.findAllResources((User) subject)
+                : ((Group) subject).getResources();
+        for (ExternalResource resource : iterable) {
             if ((type == AttributableType.USER && resource.getUmapping() != null)
                     || (type == AttributableType.GROUP && resource.getGmapping() != null)) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/22839bf3/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 6cadc9d..efe7771 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
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.core.provisioning.java.data;
 
-import static org.apache.syncope.core.provisioning.java.data.AbstractAttributableDataBinder.LOG;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -49,6 +47,9 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.common.lib.types.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 import org.apache.syncope.core.misc.ConnObjectUtils;
+import org.apache.syncope.core.misc.search.SearchCondConverter;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.DynGroupMembership;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
@@ -89,6 +90,25 @@ public class GroupDataBinderImpl extends AbstractAttributableDataBinder implemen
         }
     }
 
+    private void setDynMembership(final Group group, final String dynMembershipFIQL) {
+        SearchCond dynMembershipCond = SearchCondConverter.convert(dynMembershipFIQL);
+        if (!dynMembershipCond.isValid()) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
+            sce.getElements().add(dynMembershipFIQL);
+            throw sce;
+        }
+
+        DynGroupMembership dynMembership;
+        if (group.getDynMembership() == null) {
+            dynMembership = entityFactory.newEntity(DynGroupMembership.class);
+            dynMembership.setGroup(group);
+            group.setDynMembership(dynMembership);
+        } else {
+            dynMembership = group.getDynMembership();
+        }
+        dynMembership.setFIQLCond(dynMembershipFIQL);
+    }
+
     @Override
     public Group create(final Group group, final GroupTO groupTO) {
         SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
@@ -132,6 +152,10 @@ public class GroupDataBinderImpl extends AbstractAttributableDataBinder implemen
             }
         }
 
+        if (groupTO.getDynMembershipCond() != null) {
+            setDynMembership(group, groupTO.getDynMembershipCond());
+        }
+
         return group;
     }
 
@@ -202,6 +226,18 @@ public class GroupDataBinderImpl extends AbstractAttributableDataBinder implemen
             }
         }
 
+        // dynamic membership
+        if (group.getDynMembership() != null && groupMod.getDynMembershipCond() == null) {
+            group.setDynMembership(null);
+        } else if (group.getDynMembership() == null && groupMod.getDynMembershipCond() != null) {
+            setDynMembership(group, groupMod.getDynMembershipCond());
+        } else if (group.getDynMembership() != null && groupMod.getDynMembershipCond() != null
+                && !group.getDynMembership().getFIQLCond().equals(groupMod.getDynMembershipCond())) {
+
+            group.getDynMembership().getUsers().clear();
+            setDynMembership(group, groupMod.getDynMembershipCond());
+        }
+
         return propByRes;
     }
 
@@ -251,6 +287,10 @@ public class GroupDataBinderImpl extends AbstractAttributableDataBinder implemen
             groupTO.getMVirAttrTemplates().add(template.getSchema().getKey());
         }
 
+        if (group.getDynMembership() != null) {
+            groupTO.setDynMembershipCond(group.getDynMembership().getFIQLCond());
+        }
+
         return groupTO;
     }
 


Mime
View raw message