diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java index c92aa39317..19a6b8a0d5 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java @@ -38,9 +38,13 @@ public interface GroupDAO extends AnyDAO { Optional findByName(String name); + List findKeysByNamePattern(String pattern); + @Override Collection findAllResourceKeys(String key); + Map countByRealm(); + /** * Checks if the calling user is authorized to access the Group matching the provided key, under the given * realm. @@ -51,33 +55,73 @@ public interface GroupDAO extends AnyDAO { */ void securityChecks(Set authRealms, String key, String realm); - boolean isManager(String key); - - List findManagedUsers(String key); - - List findManagedGroups(String key); - - List findManagedAnyObjects(String key); - - Map countByRealm(); + long countUMembers(String groupKey); - List findKeysByNamePattern(String pattern); + List findUMembers(String groupKey); - List findAMemberships(Group group); + boolean existsUMembership(String userKey, String groupKey); List findUMemberships(Group group, Pageable pageable); - List findAMembers(String groupKey); + long countAMembers(String groupKey); - List findUMembers(String groupKey); + List findAMembers(String groupKey); boolean existsAMembership(String anyObjectKey, String groupKey); - boolean existsUMembership(String userKey, String groupKey); + List findAMemberships(Group group); - long countAMembers(String groupKey); + List findTypeExtensions(AnyTypeClass anyTypeClass); - long countUMembers(String groupKey); + boolean isManager(String key); - List findTypeExtensions(AnyTypeClass anyTypeClass); + /** + * Returns all users managed by the group for the given key. + * + * Given: + * * group G1 for the provided key + * * group G2, with user member U + * + * then we have 2 cases where U is managed by G1: + * + * (a) U has gManager set to G1 + * (b) G2 has gManager set to G1 + * + * @param key manager key + * @return users managed by the group for the given key + */ + List findManagedUsers(String key); + + /** + * Returns all groups managed by the group for the given key. + * + * Given: + * * group G1 for the provided key + * * group G2 + * + * then we have 1 case where G2 is managed by G1: + * + * (a) G2 has gManager set to G1 + * + * @param key manager key + * @return groups managed by the group for the given key + */ + List findManagedGroups(String key); + + /** + * Returns all any objects managed by the group for the given key. + * + * Given: + * * group G1 for the provided key + * * group G2, with any object member O + * + * then we have 2 cases where O is managed by G1: + * + * (a) O has gManager set to G1 + * (b) G2 has gManager set to G1 + * + * @param key manager key + * @return any objects managed by the group for the given key + */ + List findManagedAnyObjects(String key); } 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 38adb15061..742976fc1a 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 @@ -51,10 +51,59 @@ public interface UserDAO extends AnyDAO { boolean isManager(String key); + /** + * Returns all users managed by the user for the given key. + * + * Given: + * * user U1 for the provided key, member of group G1 + * * user U2, member of group G2 + * + * then we have 4 cases where U2 is managed by U1: + * + * (a) U2 has uManager set to U1 + * (b) G2 has uManager set to U1 + * (c) U2 has gManager set to G1 + * (d) G2 has gManager set to G1 + * + * @param key manager key + * @return users managed by the user for the given key + */ List findManagedUsers(String key); + /** + * Returns all groups managed by the user for the given key. + * + * Given: + * * user U1 for the provided key, member of group G1 + * * group G2 + * + * then we have 2 cases where G2 is managed by U1: + * + * (a) G2 has uManager set to U1 + * (b) G2 has gManager set to G1 + * + * @param key manager key + * @return groups managed by the user for the given key + */ List findManagedGroups(String key); + /** + * Returns all any objects managed by the user for the given key. + * + * Given: + * * user U for the provided key, member of group G1 + * * any object O, member of group G2 + * + * then we have 4 cases where O is managed by U: + * + * (a) O has uManager set to U + * (b) G2 has uManager set to U + * (c) O has gManager set to G1 + * (d) G2 has gManager set to G1 + * + * @param key manager key + * @return any objects managed by the user for the given key + */ List findManagedAnyObjects(String key); Map countByRealm(); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepo.java index d992d45aa0..a49692428e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepo.java @@ -41,20 +41,4 @@ public interface GroupRepo @Query("SELECT e FROM #{#entityName} e WHERE e.id IN (:keys)") @Override List findByKeys(@Param("keys") List keys); - - @Query("SELECT DISTINCT e.leftEnd.id FROM JPAAMembership e WHERE e.rightEnd.id = :groupKey") - @Override - List findAMembers(@Param("groupKey") String groupKey); - - @Query("SELECT DISTINCT e.leftEnd.id FROM JPAUMembership e WHERE e.rightEnd.id = :groupKey") - @Override - List findUMembers(@Param("groupKey") String groupKey); - - @Query("SELECT COUNT(DISTINCT e.leftEnd.id) FROM JPAAMembership e WHERE e.rightEnd.id = :groupKey") - @Override - long countAMembers(@Param("groupKey") String groupKey); - - @Query("SELECT COUNT(DISTINCT e.leftEnd.id) FROM JPAUMembership e WHERE e.rightEnd.id = :groupKey") - @Override - long countUMembers(@Param("groupKey") String groupKey); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java index 2a4cd46d99..e161420c32 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java @@ -32,28 +32,36 @@ public interface GroupRepoExt extends AnyRepoExt { + Map countByRealm(); + void securityChecks(Set authRealms, String key, String realm); - boolean isManager(String key); + long countUMembers(String groupKey); - List findManagedUsers(String key); + List findUMembers(String groupKey); - List findManagedGroups(String key); + boolean existsUMembership(String userKey, String groupKey); - List findManagedAnyObjects(String key); + List findUMemberships(Group group, Pageable pageable); - Map countByRealm(); + long countAMembers(String groupKey); - boolean existsAMembership(String anyObjectKey, String groupKey); + List findAMembers(String groupKey); - boolean existsUMembership(String userKey, String groupKey); + boolean existsAMembership(String anyObjectKey, String groupKey); List findAMemberships(Group group); - List findUMemberships(Group group, Pageable pageable); - List findTypeExtensions(AnyTypeClass anyTypeClass); + boolean isManager(String key); + + List findManagedUsers(String key); + + List findManagedGroups(String key); + + List findManagedAnyObjects(String key); + @Override S save(S group); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java index ba53327d3f..8ccbbcf749 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java @@ -21,12 +21,15 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import jakarta.persistence.TypedQuery; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; @@ -88,6 +91,25 @@ public GroupRepoExtImpl( this.anyObjectDAO = anyObjectDAO; } + @Override + public Collection findAllResourceKeys(final String key) { + return findById(key).map(Any::getResources). + orElseGet(List::of). + stream().map(ExternalResource::getKey).toList(); + } + + @Override + public Map countByRealm() { + Query query = entityManager.createQuery( + "SELECT e.realm, COUNT(e) FROM " + anyUtils.anyClass().getSimpleName() + " e GROUP BY e.realm"); + + @SuppressWarnings("unchecked") + List results = query.getResultList(); + return results.stream().collect(Collectors.toMap( + result -> ((Realm) result[0]).getFullPath(), + result -> ((Number) result[1]).longValue())); + } + @Transactional(readOnly = true) @Override public void securityChecks( @@ -120,86 +142,77 @@ protected void securityChecks(final Group group) { } @Override - public boolean isManager(final String key) { - Query user = entityManager.createNativeQuery( - "SELECT COUNT(*) FROM " + JPAUser.TABLE + " WHERE gManager_id=?"); - user.setParameter(1, key); - - Query group = entityManager.createNativeQuery( - "SELECT COUNT(*) FROM " + JPAGroup.TABLE + " WHERE gManager_id=?"); - group.setParameter(1, key); - - Query anyObject = entityManager.createNativeQuery( - "SELECT COUNT(*) FROM " + JPAAnyObject.TABLE + " WHERE gManager_id=?"); - anyObject.setParameter(1, key); + public long countUMembers(final String groupKey) { + Query query = entityManager.createNativeQuery( + "SELECT COUNT(DISTINCT user_id) FROM " + JPAUMembership.TABLE + " WHERE group_id=?"); + query.setParameter(1, groupKey); - return ((Number) user.getSingleResult()).longValue() - + ((Number) group.getSingleResult()).longValue() - + ((Number) anyObject.getSingleResult()).longValue() > 0; + return ((Number) query.getSingleResult()).longValue(); } @Override - public List findManagedUsers(final String key) { - TypedQuery query = entityManager.createQuery( - "SELECT e FROM " + JPAUser.class.getSimpleName() + " e WHERE e.gManager.id=:key", User.class); - query.setParameter("key", key); - return query.getResultList(); + public List findUMembers(final String groupKey) { + Query query = entityManager.createNativeQuery( + "SELECT DISTINCT user_id FROM " + JPAUMembership.TABLE + " WHERE group_id=?"); + query.setParameter(1, groupKey); + + @SuppressWarnings("unchecked") + List result = query.getResultList(); + return result.stream().map(String.class::cast).toList(); } @Override - public List findManagedGroups(final String key) { - TypedQuery query = entityManager.createQuery( - "SELECT e FROM " + JPAGroup.class.getSimpleName() + " e WHERE e.gManager.id=:key", Group.class); - query.setParameter("key", key); - return query.getResultList(); + public boolean existsUMembership(final String userKey, final String groupKey) { + Query query = entityManager.createNativeQuery( + "SELECT COUNT(*) FROM " + JPAUMembership.TABLE + " WHERE group_id=? AND user_id=?"); + query.setParameter(1, groupKey); + query.setParameter(2, userKey); + + return ((Number) query.getSingleResult()).longValue() > 0; } @Override - public List findManagedAnyObjects(final String key) { - TypedQuery query = entityManager.createQuery( - "SELECT e FROM " + JPAAnyObject.class.getSimpleName() + " e WHERE e.gManager.id=:key", AnyObject.class); - query.setParameter("key", key); + public List findUMemberships(final Group group, final Pageable pageable) { + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPAUMembership.class.getSimpleName() + + " e WHERE e.rightEnd=:group ORDER BY e.leftEnd", + UMembership.class); + query.setParameter("group", group); + if (pageable.isPaged()) { + query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber()); + query.setMaxResults(pageable.getPageSize()); + } + return query.getResultList(); } @Override - public Map countByRealm() { - Query query = entityManager.createQuery( - "SELECT e.realm, COUNT(e) FROM " + anyUtils.anyClass().getSimpleName() + " e GROUP BY e.realm"); + public long countAMembers(final String groupKey) { + Query query = entityManager.createNativeQuery( + "SELECT COUNT(DISTINCT anyObject_id) FROM " + JPAAMembership.TABLE + " WHERE group_id=?"); + query.setParameter(1, groupKey); - @SuppressWarnings("unchecked") - List results = query.getResultList(); - return results.stream().collect(Collectors.toMap( - result -> ((Realm) result[0]).getFullPath(), - result -> ((Number) result[1]).longValue())); - } + return ((Number) query.getSingleResult()).longValue(); - @Transactional(readOnly = true) - @Override - public Collection findAllResourceKeys(final String key) { - return findById(key).map(Any::getResources). - orElseGet(List::of). - stream().map(ExternalResource::getKey).toList(); } - @Transactional(readOnly = true) @Override - public boolean existsAMembership(final String anyObjectKey, final String groupKey) { + public List findAMembers(final String groupKey) { Query query = entityManager.createNativeQuery( - "SELECT COUNT(*) FROM " + JPAAMembership.TABLE + " WHERE group_id=? AND anyobject_it=?"); + "SELECT DISTINCT anyObject_id FROM " + JPAAMembership.TABLE + " WHERE group_id=?"); query.setParameter(1, groupKey); - query.setParameter(2, anyObjectKey); - return ((Number) query.getSingleResult()).longValue() > 0; + @SuppressWarnings("unchecked") + List result = query.getResultList(); + return result.stream().map(String.class::cast).toList(); } - @Transactional(readOnly = true) @Override - public boolean existsUMembership(final String userKey, final String groupKey) { + public boolean existsAMembership(final String anyObjectKey, final String groupKey) { Query query = entityManager.createNativeQuery( - "SELECT COUNT(*) FROM " + JPAUMembership.TABLE + " WHERE group_id=? AND user_id=?"); + "SELECT COUNT(*) FROM " + JPAAMembership.TABLE + " WHERE group_id=? AND anyobject_it=?"); query.setParameter(1, groupKey); - query.setParameter(2, userKey); + query.setParameter(2, anyObjectKey); return ((Number) query.getSingleResult()).longValue() > 0; } @@ -215,18 +228,75 @@ public List findAMemberships(final Group group) { } @Override - public List findUMemberships(final Group group, final Pageable pageable) { - TypedQuery query = entityManager.createQuery( - "SELECT e FROM " + JPAUMembership.class.getSimpleName() - + " e WHERE e.rightEnd=:group ORDER BY e.leftEnd", - UMembership.class); - query.setParameter("group", group); - if (pageable.isPaged()) { - query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber()); - query.setMaxResults(pageable.getPageSize()); - } + public boolean isManager(final String key) { + Query user = entityManager.createNativeQuery( + "SELECT COUNT(*) FROM " + JPAUser.TABLE + " WHERE gManager_id=?"); + user.setParameter(1, key); - return query.getResultList(); + Query group = entityManager.createNativeQuery( + "SELECT COUNT(*) FROM " + JPAGroup.TABLE + " WHERE gManager_id=?"); + group.setParameter(1, key); + + Query anyObject = entityManager.createNativeQuery( + "SELECT COUNT(*) FROM " + JPAAnyObject.TABLE + " WHERE gManager_id=?"); + anyObject.setParameter(1, key); + + return ((Number) user.getSingleResult()).longValue() + + ((Number) group.getSingleResult()).longValue() + + ((Number) anyObject.getSingleResult()).longValue() > 0; + } + + @Override + public List findManagedUsers(final String key) { + List result = new ArrayList<>(); + + // (a) see GroupDAO#findManagedUsers + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPAUser.class.getSimpleName() + " e WHERE e.gManager.id=:key", User.class); + query.setParameter("key", key); + result.addAll(query.getResultList()); + + // (b) see GroupDAO#findManagedUsers + findManagedGroupKeys(key).forEach(group -> findUMembers(group). + forEach(m -> userDAO.findById(m).ifPresent(result::add))); + + return result.stream().distinct().toList(); + } + + protected Stream findManagedGroupKeys(final String key) { + Query query = entityManager.createNativeQuery( + "SELECT DISTINCT id FROM " + JPAGroup.TABLE + " WHERE gManager_id=?"); + query.setParameter(1, key); + + @SuppressWarnings("unchecked") + List result = query.getResultList(); + return result.stream().map(String.class::cast); + } + + @Override + public List findManagedGroups(final String key) { + return findManagedGroupKeys(key). + map(group -> Optional.ofNullable(entityManager.find(JPAGroup.class, group))). + flatMap(Optional::stream). + map(Group.class::cast). + toList(); + } + + @Override + public List findManagedAnyObjects(final String key) { + List result = new ArrayList<>(); + + // (a) see GroupDAO#findManagedAnyObjects + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPAAnyObject.class.getSimpleName() + " e WHERE e.gManager.id=:key", AnyObject.class); + query.setParameter("key", key); + result.addAll(query.getResultList()); + + // (b) see GroupDAO#findManagedUsers + findManagedGroupKeys(key).forEach(group -> findAMembers(group). + forEach(m -> anyObjectDAO.findById(m).ifPresent(result::add))); + + return result.stream().distinct().toList(); } @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java index 3c0fa93ebe..3c4e0405bb 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java @@ -30,6 +30,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; @@ -47,6 +48,7 @@ import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject; import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup; import org.apache.syncope.core.persistence.jpa.entity.user.JPALinkedAccount; @@ -112,74 +114,85 @@ public boolean isManager(final String key) { return ((Number) user.getSingleResult()).longValue() + ((Number) group.getSingleResult()).longValue() + ((Number) anyObject.getSingleResult()).longValue() > 0; + + } + + protected Stream findUMembershipGroups(final String key) { + Query query = entityManager.createNativeQuery( + "SELECT DISTINCT group_id FROM " + JPAUMembership.TABLE + " WHERE user_id=?"); + query.setParameter(1, key); + + @SuppressWarnings("unchecked") + List groups = query.getResultList(); + return groups.stream().map(String.class::cast); } @Override public List findManagedUsers(final String key) { List result = new ArrayList<>(); - // 1. users having uManager set to the requested user + // (a) see UserDAO#findManagedUsers TypedQuery users = entityManager.createQuery( "SELECT e FROM " + JPAUser.class.getSimpleName() + " e WHERE e.uManager.id=:key", User.class); users.setParameter("key", key); result.addAll(users.getResultList()); - // 2. user members of groups having uManager set to the requested user + // (b) see UserDAO#findManagedUsers findManagedGroupKeys(key).forEach(group -> groupDAO.findUMembers(group). - forEach(member -> Optional.ofNullable(entityManager.find(JPAUser.class, member)). - ifPresent(result::add))); + forEach(m -> Optional.ofNullable(entityManager.find(JPAUser.class, m)).ifPresent(result::add))); - // 3. users managed by groups the requested user is member of - Query query = entityManager.createNativeQuery( - "SELECT DISTINCT group_id FROM " + JPAUMembership.TABLE + " WHERE user_id=?"); - query.setParameter(1, key); - @SuppressWarnings("unchecked") - List groups = query.getResultList(); - groups.stream().map(String.class::cast).forEach(group -> result.addAll(groupDAO.findManagedUsers(group))); + // (c) see UserDAO#findManagedUsers + // (d) see UserDAO#findManagedUsers + findUMembershipGroups(key).forEach(group -> result.addAll(groupDAO.findManagedUsers(group))); return result.stream().distinct().toList(); } - protected List findManagedGroupKeys(final String key) { + protected Stream findManagedGroupKeys(final String key) { Query query = entityManager.createNativeQuery( "SELECT DISTINCT id FROM " + JPAGroup.TABLE + " WHERE uManager_id=?"); query.setParameter(1, key); @SuppressWarnings("unchecked") List result = query.getResultList(); - return result.stream().map(String.class::cast).toList(); + return result.stream().map(String.class::cast); } @Override public List findManagedGroups(final String key) { - return findManagedGroupKeys(key).stream(). - map(group -> Optional.ofNullable(entityManager.find(JPAGroup.class, group))). - flatMap(Optional::stream). - map(Group.class::cast). - toList(); + List result = new ArrayList<>(); + + // (a) see UserDAO#findManagedGroups + findManagedGroupKeys(key).forEach(group -> groupDAO.findById(group).ifPresent(result::add)); + + // (b) see UserDAO#findManagedGroups + findUMembershipGroups(key).forEach(g -> result.addAll(groupDAO.findManagedGroups(g))); + + return result.stream().distinct().toList(); } @Override public List findManagedAnyObjects(final String key) { List result = new ArrayList<>(); - // 1. anyObjects having uManager set to the requested user + // (a) see UserDAO#findManagedAnyObjects TypedQuery anyObjects = entityManager.createQuery( "SELECT e FROM " + JPAAnyObject.class.getSimpleName() + " e WHERE e.uManager.id=:key", AnyObject.class); anyObjects.setParameter("key", key); result.addAll(anyObjects.getResultList()); - // 2. anyObject members of groups having uManager set to the requested user - findManagedGroupKeys(key).forEach(group -> groupDAO.findUMembers(group). - forEach(member -> Optional.ofNullable(entityManager.find(JPAAnyObject.class, member)). - ifPresent(result::add))); + // (b) see UserDAO#findManagedAnyObjects + findManagedGroupKeys(key).forEach(group -> groupDAO.findAMembers(group). + forEach(m -> Optional.ofNullable(entityManager.find(JPAAnyObject.class, m)).ifPresent(result::add))); - // 3. anyObject managed by groups the requested user is member of Query query = entityManager.createNativeQuery( - "SELECT DISTINCT group_id FROM " + JPAUMembership.TABLE + " WHERE user_id=?"); + "SELECT DISTINCT group_id FROM " + JPAAMembership.TABLE + " WHERE anyObject_id=?"); query.setParameter(1, key); @SuppressWarnings("unchecked") List groups = query.getResultList(); + + // (c) see UserDAO#findManagedAnyObjects + // (d) see UserDAO#findManagedAnyObjects groups.stream().map(String.class::cast).forEach(group -> result.addAll(groupDAO.findManagedAnyObjects(group))); return result.stream().distinct().toList(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java index 6a5f487700..cb92e78d98 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java @@ -143,34 +143,6 @@ public void saveWithTwoManagers() { }); } - @Test - public void findByUManager() { - Group group = groupDAO.findById("ebf97068-aa4b-4a85-9f01-680e8c4cf227").orElseThrow(); - - User user = userDAO.findById("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow(); - - assertEquals(user, group.getuManager()); - - List managed = userDAO.findManagedGroups(user.getKey()); - assertFalse(managed.isEmpty()); - assertEquals(1, managed.size()); - assertTrue(managed.contains(group)); - } - - @Test - public void findByGManager() { - Group root = groupDAO.findByName("root").orElseThrow(); - Group group = entityFactory.newEntity(Group.class); - group.setRealm(realmDAO.getRoot()); - group.setName("error"); - group.setgManager(root); - group = groupDAO.save(group); - entityManager.flush(); - - List managed = groupDAO.findManagedGroups(root.getKey()); - assertEquals(List.of(group), managed); - } - @Test public void create() { Group group = entityFactory.newEntity(Group.class); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ManagerTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ManagerTest.java new file mode 100644 index 0000000000..f1a856bfda --- /dev/null +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ManagerTest.java @@ -0,0 +1,270 @@ +/* + * 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.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +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.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.jpa.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.transaction.annotation.Transactional; + +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) +@Transactional +class ManagerTest extends AbstractTest { + + @Autowired + private UserDAO userDAO; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private RealmDAO realmDAO; + + /** + * (a) U2 has uManager set to U1 + */ + @Test + void findUManagedUsersA() { + User vivaldi = userDAO.findByUsername("vivaldi").orElseThrow(); + assertNull(vivaldi.getgManager()); + assertNull(vivaldi.getuManager()); + + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + assertFalse(userDAO.isManager(rossini.getKey())); + + vivaldi.setuManager(rossini); + vivaldi = userDAO.save(vivaldi); + entityManager.flush(); + assertEquals(rossini, vivaldi.getuManager()); + assertTrue(userDAO.isManager(rossini.getKey())); + + List managed = userDAO.findManagedUsers(rossini.getKey()); + assertEquals(List.of(vivaldi), managed); + } + + /** + * (b) G2 has uManager set to U1 + */ + @Test + void findUManagedUsersB() { + Group artDirector = groupDAO.findByName("artDirector").orElseThrow(); + assertNull(artDirector.getuManager()); + assertNull(artDirector.getgManager()); + + User vivaldi = userDAO.findByUsername("vivaldi").orElseThrow(); + assertFalse(userDAO.isManager(vivaldi.getKey())); + + artDirector.setuManager(vivaldi); + artDirector = groupDAO.save(artDirector); + entityManager.flush(); + assertEquals(vivaldi, artDirector.getuManager()); + assertTrue(userDAO.isManager(vivaldi.getKey())); + + User puccini = userDAO.findByUsername("puccini").orElseThrow(); + assertNull(puccini.getgManager()); + assertNull(puccini.getuManager()); + assertTrue(puccini.getMembership(artDirector.getKey()).isPresent()); + + List managed = userDAO.findManagedUsers(vivaldi.getKey()); + assertEquals(List.of(puccini), managed); + } + + /** + * (c) U2 has gManager set to G1 + */ + @Test + void findUManagedUsersC() { + Group managingDirector = groupDAO.findByName("managingDirector").orElseThrow(); + assertFalse(groupDAO.isManager(managingDirector.getKey())); + + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + assertTrue(bellini.getMembership(managingDirector.getKey()).isPresent()); + assertFalse(userDAO.isManager(bellini.getKey())); + + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + assertNull(rossini.getgManager()); + assertNull(rossini.getuManager()); + + rossini.setgManager(managingDirector); + rossini = userDAO.save(rossini); + entityManager.flush(); + assertEquals(managingDirector, rossini.getgManager()); + assertTrue(groupDAO.isManager(managingDirector.getKey())); + + List managed = userDAO.findManagedUsers(bellini.getKey()); + assertEquals(List.of(rossini), managed); + } + + /** + * (d) G2 has gManager set to G1 + */ + @Test + void findUManagedUsersD() { + Group managingDirector = groupDAO.findByName("managingDirector").orElseThrow(); + assertFalse(groupDAO.isManager(managingDirector.getKey())); + + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + assertTrue(bellini.getMembership(managingDirector.getKey()).isPresent()); + assertFalse(userDAO.isManager(bellini.getKey())); + + Group otherchild = groupDAO.findByName("otherchild").orElseThrow(); + assertNull(otherchild.getgManager()); + assertNull(otherchild.getuManager()); + + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + assertTrue(rossini.getMembership(otherchild.getKey()).isPresent()); + rossini.setuManager(null); + rossini.setgManager(null); + rossini = userDAO.save(rossini); + entityManager.flush(); + assertNull(rossini.getgManager()); + assertNull(rossini.getuManager()); + + otherchild.setgManager(managingDirector); + otherchild = groupDAO.save(otherchild); + entityManager.flush(); + assertEquals(managingDirector, otherchild.getgManager()); + assertTrue(groupDAO.isManager(managingDirector.getKey())); + + List managed = userDAO.findManagedUsers(bellini.getKey()); + assertEquals(List.of(rossini), managed); + } + + /** + * (a) G2 has uManager set to U1 - see UserDAO#findManagedGroups + */ + @Test + void findUManagedGroupsA() { + Group director = groupDAO.findByName("director").orElseThrow(); + User puccini = userDAO.findByUsername("puccini").orElseThrow(); + assertEquals(puccini, director.getuManager()); + assertTrue(userDAO.isManager(puccini.getKey())); + + List managed = userDAO.findManagedGroups(puccini.getKey()); + assertEquals(List.of(director), managed); + } + + /** + * (b) G2 has gManager set to G1 - see UserDAO#findManagedGroups + */ + @Test + void findUManagedGroupsB() { + Group managingDirector = groupDAO.findByName("managingDirector").orElseThrow(); + assertFalse(groupDAO.isManager(managingDirector.getKey())); + + Group root = groupDAO.findByName("root").orElseThrow(); + assertNull(root.getgManager()); + assertNull(root.getuManager()); + + root.setgManager(managingDirector); + root = groupDAO.save(root); + entityManager.flush(); + assertEquals(managingDirector, root.getgManager()); + assertTrue(groupDAO.isManager(managingDirector.getKey())); + + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + assertTrue(bellini.getMembership(managingDirector.getKey()).isPresent()); + + List managed = userDAO.findManagedGroups(bellini.getKey()); + assertEquals(List.of(root), managed); + } + + /** + * (a) U has gManager set to G1 + */ + @Test + void findGManagedUsersA() { + Group citizen = groupDAO.findByName("citizen").orElseThrow(); + assertFalse(groupDAO.isManager(citizen.getKey())); + + User verdi = userDAO.findByUsername("verdi").orElseThrow(); + assertNull(verdi.getgManager()); + assertNull(verdi.getuManager()); + + verdi.setgManager(citizen); + verdi = userDAO.save(verdi); + entityManager.flush(); + assertEquals(citizen, verdi.getgManager()); + assertTrue(groupDAO.isManager(citizen.getKey())); + + List managed = groupDAO.findManagedUsers(citizen.getKey()); + assertEquals(List.of(verdi), managed); + } + + /** + * (b) G2 has gManager set to G1 + */ + @Test + void findGManagedUsersB() { + Group managingDirector = groupDAO.findByName("managingDirector").orElseThrow(); + assertFalse(groupDAO.isManager(managingDirector.getKey())); + + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + assertTrue(bellini.getMembership(managingDirector.getKey()).isPresent()); + + Group citizen = groupDAO.findByName("citizen").orElseThrow(); + assertNull(citizen.getgManager()); + assertNull(citizen.getuManager()); + + User verdi = userDAO.findByUsername("verdi").orElseThrow(); + assertTrue(verdi.getMembership(citizen.getKey()).isPresent()); + + citizen.setgManager(managingDirector); + citizen = groupDAO.save(citizen); + entityManager.flush(); + assertEquals(managingDirector, citizen.getgManager()); + assertTrue(groupDAO.isManager(managingDirector.getKey())); + + List managed = groupDAO.findManagedUsers(managingDirector.getKey()); + assertEquals(List.of(verdi), managed); + } + + /** + * (a) G2 has gManager set to G1 - see GroupDAO#findManagedGroups + */ + @Test + void findGManagedGroups() { + Group root = groupDAO.findByName("root").orElseThrow(); + assertFalse(groupDAO.isManager(root.getKey())); + + Group group = entityFactory.newEntity(Group.class); + group.setRealm(realmDAO.getRoot()); + group.setName("error"); + group.setgManager(root); + group = groupDAO.save(group); + entityManager.flush(); + assertEquals(root, group.getgManager()); + assertTrue(groupDAO.isManager(root.getKey())); + + List managed = groupDAO.findManagedGroups(root.getKey()); + assertEquals(List.of(group), managed); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepo.java index c6351fb358..4bcabe8dc1 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepo.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepo.java @@ -21,11 +21,7 @@ import java.util.List; import java.util.Optional; import org.apache.syncope.core.persistence.api.dao.GroupDAO; -import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; -import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; -import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUMembership; -import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; import org.springframework.data.neo4j.repository.query.Query; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; @@ -40,46 +36,4 @@ public interface GroupRepo @Query("MATCH (n:" + Neo4jGroup.NODE + ") WHERE toLower(n.name) =~ $pattern RETURN n.id") @Override List findKeysByNamePattern(@Param("pattern") String pattern); - - @Query("MATCH (a:" + Neo4jAnyObject.NODE + " {id: $anyObjectKey})-[]-" - + "(n:" + Neo4jAMembership.NODE + ")-[]-" - + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " - + "RETURN COUNT(n) > 0") - @Override - boolean existsAMembership(String anyObjectKey, String groupKey); - - @Query("MATCH (u:" + Neo4jUser.NODE + " {id: $userKey})-[]-" - + "(n:" + Neo4jUMembership.NODE + ")-[]-" - + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " - + "RETURN COUNT(n) > 0") - @Override - boolean existsUMembership(String userKey, String groupKey); - - @Query("MATCH (a:" + Neo4jAnyObject.NODE + ")-[]-" - + "(n:" + Neo4jAMembership.NODE + ")-[]-" - + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " - + "RETURN a.id") - @Override - List findAMembers(@Param("groupKey") String groupKey); - - @Query("MATCH (u:" + Neo4jUser.NODE + ")-[]-" - + "(n:" + Neo4jUMembership.NODE + ")-[]-" - + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " - + "RETURN u.id") - @Override - List findUMembers(@Param("groupKey") String groupKey); - - @Query("MATCH (a:" + Neo4jAnyObject.NODE + ")-[]-" - + "(n:" + Neo4jAMembership.NODE + ")-[]-" - + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " - + "RETURN COUNT(DISTINCT a)") - @Override - long countAMembers(@Param("groupKey") String groupKey); - - @Query("MATCH (u:" + Neo4jUser.NODE + ")-[]-" - + "(n:" + Neo4jUMembership.NODE + ")-[]-" - + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " - + "RETURN COUNT(DISTINCT u)") - @Override - long countUMembers(@Param("groupKey") String groupKey); } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java index 3adafe726f..7e3cfeae95 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java @@ -34,24 +34,36 @@ public interface GroupRepoExt extends AnyRepoExt { String CACHE = "groupCache"; + Map countByRealm(); + void securityChecks(Set authRealms, String key, String realm); - boolean isManager(String key); + long countUMembers(String groupKey); - List findManagedUsers(String key); + List findUMembers(String groupKey); - List findManagedGroups(String key); + boolean existsUMembership(String userKey, String groupKey); - List findManagedAnyObjects(String key); + List findUMemberships(Group group, Pageable pageable); - Map countByRealm(); + long countAMembers(String groupKey); - List findAMemberships(Group group); + List findAMembers(String groupKey); - List findUMemberships(Group group, Pageable pageable); + boolean existsAMembership(String anyObjectKey, String groupKey); + + List findAMemberships(Group group); List findTypeExtensions(AnyTypeClass anyTypeClass); + boolean isManager(String key); + + List findManagedUsers(String key); + + List findManagedGroups(String key); + + List findManagedAnyObjects(String key); + @Override S save(S group); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java index f42f1573d0..50f0e5c145 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java @@ -19,6 +19,7 @@ package org.apache.syncope.core.persistence.neo4j.dao.repo; import java.time.OffsetDateTime; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; @@ -26,6 +27,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.cache.Cache; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; @@ -127,12 +129,27 @@ protected Cache cache() { return groupCache; } - @Transactional(readOnly = true) @Override public Optional findLastChange(final String key) { return findLastChange(key, Neo4jGroup.NODE); } + @Override + public Collection findAllResourceKeys(final String key) { + return findById(key).map(Any::getResources). + orElseGet(List::of). + stream().map(ExternalResource::getKey).toList(); + } + + @Override + public Map countByRealm() { + Collection> result = neo4jClient.query( + "MATCH (n:" + Neo4jGroup.NODE + ")-[]-(r:" + Neo4jRealm.NODE + ") " + + "RETURN r.fullPath AS realm, COUNT(n) AS counted").fetch().all(); + + return result.stream().collect(Collectors.toMap(r -> r.get("realm").toString(), r -> (Long) r.get("counted"))); + } + @Transactional(readOnly = true) @Override public void securityChecks( @@ -157,6 +174,94 @@ public void securityChecks( } } + @Override + public long countUMembers(final String groupKey) { + return neo4jTemplate.count( + "MATCH (u:" + Neo4jUser.NODE + ")-[]-" + + "(n:" + Neo4jUMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN COUNT(DISTINCT u)", + Map.of("groupKey", groupKey)); + } + + @Override + public List findUMembers(final String groupKey) { + Collection> result = neo4jClient.query( + "MATCH (u:" + Neo4jUser.NODE + ")-[]-" + + "(n:" + Neo4jUMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN u.id").bindAll(Map.of("groupKey", groupKey)).fetch().all(); + return result.stream().map(found -> found.get("u.id").toString()).toList(); + } + + @Override + public boolean existsUMembership(final String userKey, final String groupKey) { + return neo4jTemplate.count( + "MATCH (u:" + Neo4jUser.NODE + " {id: $userKey})-[]-" + + "(n:" + Neo4jUMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN COUNT(n)", + Map.of("userKey", userKey, "groupKey", groupKey)) > 0; + } + + @Override + public List findUMemberships(final Group group, final Pageable pageable) { + String paged = ""; + if (pageable.isPaged()) { + paged = " SKIP " + pageable.getPageSize() * pageable.getPageNumber() + + " LIMIT " + pageable.getPageSize(); + } + return toList( + neo4jClient.query( + "MATCH (n:" + Neo4jUMembership.NODE + ")-[]-(g:" + Neo4jGroup.NODE + " {id: $id}) " + + "RETURN n.id" + paged) + .bindAll(Map.of("id", group.getKey())).fetch().all(), + "n.id", + Neo4jUMembership.class, + null); + } + + @Override + public long countAMembers(final String groupKey) { + return neo4jTemplate.count( + "MATCH (a:" + Neo4jAnyObject.NODE + ")-[]-" + + "(n:" + Neo4jAMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN COUNT(DISTINCT a)", + Map.of("groupKey", groupKey)); + } + + @Override + public List findAMembers(final String groupKey) { + Collection> result = neo4jClient.query( + "MATCH (a:" + Neo4jAnyObject.NODE + ")-[]-" + + "(n:" + Neo4jAMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN a.id").bindAll(Map.of("groupKey", groupKey)).fetch().all(); + return result.stream().map(found -> found.get("a.id").toString()).toList(); + } + + @Override + public boolean existsAMembership(final String anyObjectKey, final String groupKey) { + return neo4jTemplate.count( + "MATCH (a:" + Neo4jAnyObject.NODE + " {id: $anyObjectKey})-[]-" + + "(n:" + Neo4jAMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN COUNT(n)", + Map.of("anyObjectKey", anyObjectKey, "groupKey", groupKey)) > 0; + } + + @Override + public List findAMemberships(final Group group) { + return toList( + neo4jClient.query( + "MATCH (n:" + Neo4jAMembership.NODE + ")-[]-(g:" + Neo4jGroup.NODE + " {id: $id}) " + + "RETURN n.id").bindAll(Map.of("id", group.getKey())).fetch().all(), + "n.id", + Neo4jAMembership.class, + null); + } + @Override protected void securityChecks(final Group group) { Set authRealms = AuthContextUtils.getAuthorizations(). @@ -167,90 +272,87 @@ protected void securityChecks(final Group group) { @Override public boolean isManager(final String key) { - return !findManagedUsers(key).isEmpty() - || !findManagedGroups(key).isEmpty() - || !findManagedAnyObjects(key).isEmpty(); + long users = neo4jTemplate.count( + "MATCH (n:" + Neo4jGroup.NODE + " {id: $key})-" + + "[:" + AbstractAny.GROUP_MANAGER_REL + "]-" + + "(p:" + Neo4jUser.NODE + ") " + + "RETURN COUNT(p.id)", + Map.of("key", key)); + + long groups = neo4jTemplate.count( + "MATCH (n:" + Neo4jGroup.NODE + " {id: $key})-" + + "[:" + AbstractAny.GROUP_MANAGER_REL + "]-" + + "(p:" + Neo4jGroup.NODE + ") " + + "RETURN COUNT(p.id)", + Map.of("key", key)); + + long anyObjects = neo4jTemplate.count( + "MATCH (n:" + Neo4jGroup.NODE + " {id: $key})-" + + "[:" + AbstractAny.GROUP_MANAGER_REL + "]-" + + "(p:" + Neo4jAnyObject.NODE + ") " + + "RETURN COUNT(p.id)", + Map.of("key", key)); + + return users + groups + anyObjects > 0; } @Override public List findManagedUsers(final String key) { - return toList(neo4jClient.query( + List result = new ArrayList<>(); + + // (a) see GroupDAO#findManagedUsers + result.addAll(toList(neo4jClient.query( "MATCH (n:" + Neo4jGroup.NODE + " {id: $id})-" + "[:" + AbstractAny.GROUP_MANAGER_REL + "]-" + "(p:" + Neo4jUser.NODE + ") " + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(), "p.id", Neo4jUser.class, - userCache); + userCache)); + + // (b) see GroupDAO#findManagedUsers + findManagedGroupKeys(key).forEach(group -> findUMembers(group). + forEach(m -> userDAO.findById(m).ifPresent(result::add))); + + return result.stream().distinct().toList(); } - @Override - public List findManagedGroups(final String key) { - return toList(neo4jClient.query( + protected Stream findManagedGroupKeys(final String key) { + Collection> result = neo4jClient.query( "MATCH (n:" + Neo4jGroup.NODE + " {id: $id})-" + "[:" + AbstractAny.GROUP_MANAGER_REL + "]-" + "(p:" + Neo4jGroup.NODE + ") " - + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(), - "p.id", - Neo4jGroup.class, - groupCache); + + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(); + return result.stream().map(found -> found.get("p.id").toString()); + } + + @Override + public List findManagedGroups(final String key) { + return findManagedGroupKeys(key).map(group -> findById(group, Neo4jGroup.class, groupCache)). + flatMap(Optional::stream). + map(Group.class::cast). + toList(); } @Override public List findManagedAnyObjects(final String key) { - return toList(neo4jClient.query( + List result = new ArrayList<>(); + + // (a) see GroupDAO#findManagedAnyObjects + result.addAll(toList(neo4jClient.query( "MATCH (n:" + Neo4jGroup.NODE + " {id: $id})-" + "[:" + AbstractAny.GROUP_MANAGER_REL + "]-" + "(p:" + Neo4jGroup.NODE + ") " + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(), "p.id", Neo4jAnyObject.class, - anyObjectCache); - } - - @Override - public Map countByRealm() { - Collection> result = neo4jClient.query( - "MATCH (n:" + Neo4jGroup.NODE + ")-[]-(r:" + Neo4jRealm.NODE + ") " - + "RETURN r.fullPath AS realm, COUNT(n) AS counted").fetch().all(); - - return result.stream().collect(Collectors.toMap(r -> r.get("realm").toString(), r -> (Long) r.get("counted"))); - } - - @Transactional(readOnly = true) - @Override - public Collection findAllResourceKeys(final String key) { - return findById(key).map(Any::getResources). - orElseGet(List::of). - stream().map(ExternalResource::getKey).toList(); - } + anyObjectCache)); - @Override - public List findAMemberships(final Group group) { - return toList( - neo4jClient.query( - "MATCH (n:" + Neo4jAMembership.NODE + ")-[]-(g:" + Neo4jGroup.NODE + " {id: $id}) " - + "RETURN n.id").bindAll(Map.of("id", group.getKey())).fetch().all(), - "n.id", - Neo4jAMembership.class, - null); - } + // (b) see GroupDAO#findManagedUsers + findManagedGroupKeys(key).forEach(group -> findAMembers(group). + forEach(m -> anyObjectDAO.findById(m).ifPresent(result::add))); - @Override - public List findUMemberships(final Group group, final Pageable pageable) { - String paged = ""; - if (pageable.isPaged()) { - paged = " SKIP " + pageable.getPageSize() * pageable.getPageNumber() - + " LIMIT " + pageable.getPageSize(); - } - return toList( - neo4jClient.query( - "MATCH (n:" + Neo4jUMembership.NODE + ")-[]-(g:" + Neo4jGroup.NODE + " {id: $id}) " - + "RETURN n.id" + paged) - .bindAll(Map.of("id", group.getKey())).fetch().all(), - "n.id", - Neo4jUMembership.class, - null); + return result.stream().distinct().toList(); } @Override @@ -262,18 +364,18 @@ public S save(final S group) { neo4jTemplate.findById(group.getKey(), Neo4jGroup.class).ifPresent(before -> { before.getResources().stream().filter(resource -> !group.getResources().contains(resource)). forEach(resource -> deleteRelationship( - Neo4jGroup.NODE, - Neo4jExternalResource.NODE, - group.getKey(), - resource.getKey(), - Neo4jGroup.GROUP_RESOURCE_REL)); + Neo4jGroup.NODE, + Neo4jExternalResource.NODE, + group.getKey(), + resource.getKey(), + Neo4jGroup.GROUP_RESOURCE_REL)); before.getAuxClasses().stream().filter(auxClass -> !group.getAuxClasses().contains(auxClass)). forEach(auxClass -> deleteRelationship( - Neo4jGroup.NODE, - Neo4jAnyTypeClass.NODE, - group.getKey(), - auxClass.getKey(), - Neo4jGroup.GROUP_AUX_CLASSES_REL)); + Neo4jGroup.NODE, + Neo4jAnyTypeClass.NODE, + group.getKey(), + auxClass.getKey(), + Neo4jGroup.GROUP_AUX_CLASSES_REL)); if (before.getuManager() != null && group.getuManager() == null) { deleteRelationship( Neo4jGroup.NODE, diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java index a2a1e41dd5..e497da8fbf 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java @@ -28,6 +28,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.cache.Cache; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; @@ -58,6 +59,7 @@ import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRole; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLinkedAccount; @@ -166,16 +168,44 @@ public Optional findLastChange(final String key) { @Override public boolean isManager(final String key) { - return !findManagedUsers(key).isEmpty() - || !findManagedGroups(key).isEmpty() - || !findManagedAnyObjects(key).isEmpty(); + long users = neo4jTemplate.count( + "MATCH (n:" + Neo4jUser.NODE + " {id: $key})-" + + "[:" + AbstractAny.USER_MANAGER_REL + "]-" + + "(p:" + Neo4jUser.NODE + ") " + + "RETURN COUNT(p.id)", + Map.of("key", key)); + + long groups = neo4jTemplate.count( + "MATCH (n:" + Neo4jUser.NODE + " {id: $key})-" + + "[:" + AbstractAny.USER_MANAGER_REL + "]-" + + "(p:" + Neo4jGroup.NODE + ") " + + "RETURN COUNT(p.id)", + Map.of("key", key)); + + long anyObjects = neo4jTemplate.count( + "MATCH (n:" + Neo4jUser.NODE + " {id: $key})-" + + "[:" + AbstractAny.USER_MANAGER_REL + "]-" + + "(p:" + Neo4jAnyObject.NODE + ") " + + "RETURN COUNT(p.id)", + Map.of("key", key)); + + return users + groups + anyObjects > 0; + } + + protected Stream findUMembershipGroups(final String key) { + Collection> result = neo4jClient.query( + "MATCH (u:" + Neo4jUser.NODE + " {id: $key})-[]-" + + "(n:" + Neo4jUMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + ") " + + "RETURN g.id").bindAll(Map.of("key", key)).fetch().all(); + return result.stream().map(found -> found.get("g.id").toString()); } @Override public List findManagedUsers(final String key) { List result = new ArrayList<>(); - // 1. users having uManager set to the requested user + // (a) see UserDAO#findManagedUsers result.addAll(toList(neo4jClient.query( "MATCH (n:" + Neo4jUser.NODE + " {id: $id})-" + "[:" + AbstractAny.USER_MANAGER_REL + "]-" @@ -185,35 +215,44 @@ public List findManagedUsers(final String key) { Neo4jUser.class, userCache)); - // 2. user members of groups having uManager set to the requested user - findManagedGroups(key).forEach(group -> groupDAO.findUMembers(group.getKey()). - forEach(member -> findById(member). - ifPresent(result::add))); + // (b) see UserDAO#findManagedUsers + findManagedGroupKeys(key).forEach(group -> groupDAO.findUMembers(group). + forEach(m -> findById(m).ifPresent(result::add))); - // 3. users managed by groups the requested user is member of - findById(key).ifPresent(user -> user.getMemberships(). - forEach(m -> result.addAll(groupDAO.findManagedUsers(m.getRightEnd().getKey())))); + // (c) see UserDAO#findManagedUsers + // (d) see UserDAO#findManagedUsers + findUMembershipGroups(key).forEach(group -> result.addAll(groupDAO.findManagedUsers(group))); return result.stream().distinct().toList(); } - @Override - public List findManagedGroups(final String key) { - return toList(neo4jClient.query( + protected Stream findManagedGroupKeys(final String key) { + Collection> result = neo4jClient.query( "MATCH (n:" + Neo4jUser.NODE + " {id: $id})-" + "[:" + AbstractAny.USER_MANAGER_REL + "]-" + "(p:" + Neo4jGroup.NODE + ") " - + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(), - "p.id", - Neo4jGroup.class, - groupCache); + + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(); + return result.stream().map(found -> found.get("p.id").toString()); + } + + @Override + public List findManagedGroups(final String key) { + List result = new ArrayList<>(); + + // (a) see UserDAO#findManagedGroups + findManagedGroupKeys(key).forEach(group -> groupDAO.findById(group).ifPresent(result::add)); + + // (b) see UserDAO#findManagedGroups + findUMembershipGroups(key).forEach(g -> result.addAll(groupDAO.findManagedGroups(g))); + + return result.stream().distinct().toList(); } @Override public List findManagedAnyObjects(final String key) { List result = new ArrayList<>(); - // 1. anyObjects having uManager set to the requested user + // (a) see UserDAO#findManagedAnyObjects result.addAll(toList(neo4jClient.query( "MATCH (n:" + Neo4jUser.NODE + " {id: $id})-" + "[:" + AbstractAny.USER_MANAGER_REL + "]-" @@ -223,14 +262,20 @@ public List findManagedAnyObjects(final String key) { Neo4jAnyObject.class, anyObjectCache)); - // 2. anyObject members of groups having uManager set to the requested user - findManagedGroups(key).forEach(group -> groupDAO.findAMembers(group.getKey()). - forEach(member -> findById(member, Neo4jAnyObject.class, anyObjectCache). - ifPresent(result::add))); + // (b) see UserDAO#findManagedAnyObjects + findManagedGroupKeys(key).forEach(group -> groupDAO.findAMembers(group). + forEach(m -> findById(m, Neo4jAnyObject.class, anyObjectCache).ifPresent(result::add))); + + Collection> membershipQueryResult = neo4jClient.query( + "MATCH (u:" + Neo4jAnyObject.NODE + " {id: $key})-[]-" + + "(n:" + Neo4jAMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + ") " + + "RETURN g.id").bindAll(Map.of("key", key)).fetch().all(); + Stream groups = membershipQueryResult.stream().map(found -> found.get("g.id").toString()); - // 3. anyObject managed by groups the requested user is member of - findById(key).ifPresent(user -> user.getMemberships(). - forEach(m -> result.addAll(groupDAO.findManagedAnyObjects(m.getRightEnd().getKey())))); + // (c) see UserDAO#findManagedAnyObjects + // (d) see UserDAO#findManagedAnyObjects + groups.map(String.class::cast).forEach(group -> result.addAll(groupDAO.findManagedAnyObjects(group))); return result.stream().distinct().toList(); } @@ -321,25 +366,25 @@ public S save(final S user) { neo4jTemplate.findById(user.getKey(), Neo4jUser.class).ifPresent(before -> { before.getRoles().stream().filter(role -> !user.getRoles().contains(role)). forEach(role -> deleteRelationship( - Neo4jUser.NODE, - Neo4jRole.NODE, - user.getKey(), - role.getKey(), - Neo4jUser.ROLE_MEMBERSHIP_REL)); + Neo4jUser.NODE, + Neo4jRole.NODE, + user.getKey(), + role.getKey(), + Neo4jUser.ROLE_MEMBERSHIP_REL)); before.getResources().stream().filter(resource -> !user.getResources().contains(resource)). forEach(resource -> deleteRelationship( - Neo4jUser.NODE, - Neo4jExternalResource.NODE, - user.getKey(), - resource.getKey(), - Neo4jUser.USER_RESOURCE_REL)); + Neo4jUser.NODE, + Neo4jExternalResource.NODE, + user.getKey(), + resource.getKey(), + Neo4jUser.USER_RESOURCE_REL)); before.getAuxClasses().stream().filter(auxClass -> !user.getAuxClasses().contains(auxClass)). forEach(auxClass -> deleteRelationship( - Neo4jUser.NODE, - Neo4jAnyTypeClass.NODE, - user.getKey(), - auxClass.getKey(), - Neo4jUser.USER_AUX_CLASSES_REL)); + Neo4jUser.NODE, + Neo4jAnyTypeClass.NODE, + user.getKey(), + auxClass.getKey(), + Neo4jUser.USER_AUX_CLASSES_REL)); if (before.getuManager() != null && user.getuManager() == null) { deleteRelationship( Neo4jUser.NODE, diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java index 7510fd30a9..1bb2e80edc 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java @@ -141,33 +141,6 @@ public void saveWithTwoManagers() { }); } - @Test - public void findByUManager() { - Group group = groupDAO.findById("ebf97068-aa4b-4a85-9f01-680e8c4cf227").orElseThrow(); - - User user = userDAO.findById("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow(); - - assertEquals(user, group.getuManager()); - - List managed = userDAO.findManagedGroups(user.getKey()); - assertFalse(managed.isEmpty()); - assertEquals(1, managed.size()); - assertTrue(managed.contains(group)); - } - - @Test - public void findByGManager() { - Group root = groupDAO.findByName("root").orElseThrow(); - Group group = entityFactory.newEntity(Group.class); - group.setRealm(realmDAO.getRoot()); - group.setName("error"); - group.setgManager(root); - group = groupDAO.save(group); - - List managed = groupDAO.findManagedGroups(root.getKey()); - assertEquals(List.of(group), managed); - } - @Test public void create() { Group group = entityFactory.newEntity(Group.class); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ManagerTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ManagerTest.java new file mode 100644 index 0000000000..a85728aeb4 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ManagerTest.java @@ -0,0 +1,259 @@ +/* + * 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.neo4j.outer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +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.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +class ManagerTest extends AbstractTest { + + @Autowired + private UserDAO userDAO; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private RealmDAO realmDAO; + + /** + * (a) U2 has uManager set to U1 + */ + @Test + void findUManagedUsersA() { + User vivaldi = userDAO.findByUsername("vivaldi").orElseThrow(); + assertNull(vivaldi.getgManager()); + assertNull(vivaldi.getuManager()); + + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + assertFalse(userDAO.isManager(rossini.getKey())); + + vivaldi.setuManager(rossini); + vivaldi = userDAO.save(vivaldi); + assertEquals(rossini, vivaldi.getuManager()); + assertTrue(userDAO.isManager(rossini.getKey())); + + List managed = userDAO.findManagedUsers(rossini.getKey()); + assertEquals(List.of(vivaldi), managed); + } + + /** + * (b) G2 has uManager set to U1 + */ + @Test + void findUManagedUsersB() { + Group artDirector = groupDAO.findByName("artDirector").orElseThrow(); + assertNull(artDirector.getuManager()); + assertNull(artDirector.getgManager()); + + User vivaldi = userDAO.findByUsername("vivaldi").orElseThrow(); + assertFalse(userDAO.isManager(vivaldi.getKey())); + + artDirector.setuManager(vivaldi); + artDirector = groupDAO.save(artDirector); + assertEquals(vivaldi, artDirector.getuManager()); + assertTrue(userDAO.isManager(vivaldi.getKey())); + + User puccini = userDAO.findByUsername("puccini").orElseThrow(); + assertNull(puccini.getgManager()); + assertNull(puccini.getuManager()); + assertTrue(puccini.getMembership(artDirector.getKey()).isPresent()); + + List managed = userDAO.findManagedUsers(vivaldi.getKey()); + assertEquals(List.of(puccini), managed); + } + + /** + * (c) U2 has gManager set to G1 + */ + @Test + void findUManagedUsersC() { + Group managingDirector = groupDAO.findByName("managingDirector").orElseThrow(); + assertFalse(groupDAO.isManager(managingDirector.getKey())); + + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + assertTrue(bellini.getMembership(managingDirector.getKey()).isPresent()); + assertFalse(userDAO.isManager(bellini.getKey())); + + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + assertNull(rossini.getgManager()); + assertNull(rossini.getuManager()); + + rossini.setgManager(managingDirector); + rossini = userDAO.save(rossini); + assertEquals(managingDirector, rossini.getgManager()); + assertTrue(groupDAO.isManager(managingDirector.getKey())); + + List managed = userDAO.findManagedUsers(bellini.getKey()); + assertEquals(List.of(rossini), managed); + } + + /** + * (d) G2 has gManager set to G1 + */ + @Test + void findUManagedUsersD() { + Group managingDirector = groupDAO.findByName("managingDirector").orElseThrow(); + assertFalse(groupDAO.isManager(managingDirector.getKey())); + + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + assertTrue(bellini.getMembership(managingDirector.getKey()).isPresent()); + assertFalse(userDAO.isManager(bellini.getKey())); + + Group otherchild = groupDAO.findByName("otherchild").orElseThrow(); + assertNull(otherchild.getgManager()); + assertNull(otherchild.getuManager()); + + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + assertTrue(rossini.getMembership(otherchild.getKey()).isPresent()); + rossini.setuManager(null); + rossini.setgManager(null); + rossini = userDAO.save(rossini); + assertNull(rossini.getgManager()); + assertNull(rossini.getuManager()); + + otherchild.setgManager(managingDirector); + otherchild = groupDAO.save(otherchild); + assertEquals(managingDirector, otherchild.getgManager()); + assertTrue(groupDAO.isManager(managingDirector.getKey())); + + List managed = userDAO.findManagedUsers(bellini.getKey()); + assertEquals(List.of(rossini), managed); + } + + /** + * (a) G2 has uManager set to U1 - see UserDAO#findManagedGroups + */ + @Test + void findUManagedGroupsA() { + Group director = groupDAO.findByName("director").orElseThrow(); + User puccini = userDAO.findByUsername("puccini").orElseThrow(); + assertEquals(puccini, director.getuManager()); + assertTrue(userDAO.isManager(puccini.getKey())); + + List managed = userDAO.findManagedGroups(puccini.getKey()); + assertEquals(List.of(director), managed); + } + + /** + * (b) G2 has gManager set to G1 - see UserDAO#findManagedGroups + */ + @Test + void findUManagedGroupsB() { + Group managingDirector = groupDAO.findByName("managingDirector").orElseThrow(); + assertFalse(groupDAO.isManager(managingDirector.getKey())); + + Group root = groupDAO.findByName("root").orElseThrow(); + assertNull(root.getgManager()); + assertNull(root.getuManager()); + + root.setgManager(managingDirector); + root = groupDAO.save(root); + assertEquals(managingDirector, root.getgManager()); + assertTrue(groupDAO.isManager(managingDirector.getKey())); + + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + assertTrue(bellini.getMembership(managingDirector.getKey()).isPresent()); + + List managed = userDAO.findManagedGroups(bellini.getKey()); + assertEquals(List.of(root), managed); + } + + /** + * (a) U has gManager set to G1 + */ + @Test + void findGManagedUsersA() { + Group citizen = groupDAO.findByName("citizen").orElseThrow(); + assertFalse(groupDAO.isManager(citizen.getKey())); + + User verdi = userDAO.findByUsername("verdi").orElseThrow(); + assertNull(verdi.getgManager()); + assertNull(verdi.getuManager()); + + verdi.setgManager(citizen); + verdi = userDAO.save(verdi); + assertEquals(citizen, verdi.getgManager()); + assertTrue(groupDAO.isManager(citizen.getKey())); + + List managed = groupDAO.findManagedUsers(citizen.getKey()); + assertEquals(List.of(verdi), managed); + } + + /** + * (b) G2 has gManager set to G1 + */ + @Test + void findGManagedUsersB() { + Group managingDirector = groupDAO.findByName("managingDirector").orElseThrow(); + assertFalse(groupDAO.isManager(managingDirector.getKey())); + + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + assertTrue(bellini.getMembership(managingDirector.getKey()).isPresent()); + + Group citizen = groupDAO.findByName("citizen").orElseThrow(); + assertNull(citizen.getgManager()); + assertNull(citizen.getuManager()); + + User verdi = userDAO.findByUsername("verdi").orElseThrow(); + assertTrue(verdi.getMembership(citizen.getKey()).isPresent()); + + citizen.setgManager(managingDirector); + citizen = groupDAO.save(citizen); + assertEquals(managingDirector, citizen.getgManager()); + assertTrue(groupDAO.isManager(managingDirector.getKey())); + + List managed = groupDAO.findManagedUsers(managingDirector.getKey()); + assertEquals(List.of(verdi), managed); + } + + /** + * (a) G2 has gManager set to G1 - see GroupDAO#findManagedGroups + */ + @Test + void findGManagedGroups() { + Group root = groupDAO.findByName("root").orElseThrow(); + assertFalse(groupDAO.isManager(root.getKey())); + + Group group = entityFactory.newEntity(Group.class); + group.setRealm(realmDAO.getRoot()); + group.setName("error"); + group.setgManager(root); + group = groupDAO.save(group); + assertEquals(root, group.getgManager()); + assertTrue(groupDAO.isManager(root.getKey())); + + List managed = groupDAO.findManagedGroups(root.getKey()); + assertEquals(List.of(group), managed); + } +} diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java index 7e0f3e9c66..de35fb202a 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java +++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java @@ -205,7 +205,7 @@ protected void addParameters(final List parameters) { openapiFeature.setUseContextBasedConfig(true); openapiFeature.setTitle("Apache Syncope Self Keymaster"); openapiFeature.setVersion(version); - openapiFeature.setDescription("Apache Syncope Self Keymaster" + version); + openapiFeature.setDescription("Apache Syncope Self Keymaster " + version); openapiFeature.setContactName("The Apache Syncope community"); openapiFeature.setContactEmail("dev@syncope.apache.org"); openapiFeature.setContactUrl("https://syncope.apache.org"); 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 b87d9e9d7e..2d5d907840 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 @@ -36,6 +36,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.mutable.Mutable; +import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableObject; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.keymaster.client.api.StandardConfParams; @@ -570,34 +571,44 @@ protected Set getUserAuthorities(final User user) { // Give role entitlements userDAO.findAllRoles(user).forEach(role -> role.getEntitlements(). - forEach(e -> populateEntForRealms(entForRealms, e, role.getRealms().stream().map(Realm::getFullPath). - toList()))); + forEach(e -> populateEntForRealms( + entForRealms, e, role.getRealms().stream().map(Realm::getFullPath).toList()))); // Give manager entitlements - if (userDAO.isManager(user.getKey())) { + MutableBoolean manager = new MutableBoolean(false); + userDAO.findManagedUsers(user.getKey()).forEach(managedUser -> { + USER_MANAGER_ENTITLEMENTS.forEach(e -> populateEntForRealms( + entForRealms, e, Set.of(new RealmUtils.ManagerRealm( + managedUser.getRealm().getFullPath(), + AnyTypeKind.USER, + managedUser.getKey()).output()))); + manager.setTrue(); + }); + + userDAO.findManagedGroups(user.getKey()).forEach(group -> { + GROUP_MANAGER_ENTITLEMENTS.forEach(e -> populateEntForRealms( + entForRealms, e, Set.of(new RealmUtils.ManagerRealm( + group.getRealm().getFullPath(), + AnyTypeKind.GROUP, + group.getKey()).output()))); + manager.setTrue(); + }); + + userDAO.findManagedAnyObjects(user.getKey()).forEach(anyObject -> { + ANYOBJECT_MANAGER_ENTITLEMENTS.apply(anyObject.getType().getKey()).forEach(e -> populateEntForRealms( + entForRealms, e, Set.of( + new RealmUtils.ManagerRealm( + anyObject.getRealm().getFullPath(), + AnyTypeKind.ANY_OBJECT, + anyObject.getKey()).output()))); + manager.setTrue(); + }); + + if (manager.isTrue()) { BASE_MANAGER_ENTITLEMENTS.forEach(e -> populateEntForRealms( entForRealms, e, SyncopeConstants.FULL_ADMIN_REALMS)); } - userDAO.findManagedUsers(user.getKey()).forEach(managedUser -> USER_MANAGER_ENTITLEMENTS. - forEach(e -> populateEntForRealms(entForRealms, e, Set.of(new RealmUtils.ManagerRealm( - managedUser.getRealm().getFullPath(), - AnyTypeKind.USER, - managedUser.getKey()).output())))); - - userDAO.findManagedGroups(user.getKey()).forEach(group -> GROUP_MANAGER_ENTITLEMENTS. - forEach(e -> populateEntForRealms(entForRealms, e, Set.of(new RealmUtils.ManagerRealm( - group.getRealm().getFullPath(), - AnyTypeKind.GROUP, - group.getKey()).output())))); - - userDAO.findManagedAnyObjects(user.getKey()).forEach(anyObject -> ANYOBJECT_MANAGER_ENTITLEMENTS. - apply(anyObject.getType().getKey()).forEach(e -> populateEntForRealms(entForRealms, e, Set.of( - new RealmUtils.ManagerRealm( - anyObject.getRealm().getFullPath(), - AnyTypeKind.ANY_OBJECT, - anyObject.getKey()).output())))); - return buildAuthorities(entForRealms); } diff --git a/pom.xml b/pom.xml index fdfd0aa3a0..36621e3cb1 100644 --- a/pom.xml +++ b/pom.xml @@ -2087,33 +2087,33 @@ under the License. org.apache.rat apache-rat-plugin - - **/nbactions.xml - **/nb-configuration.xml - **/META-INF/cxf/** - **/META-INF/services/** - **/META-INF/MANIFEST.MF - **/*.csv - **/archetype-resources/** - **/adminlte* - **/dataTables.bootstrap5.min.css - **/goal.txt - **/rat.txt - **/build-copy-javadoc-files.xml - **/maven-eclipse.xml - **/*.iml - **/*.log - **/.externalToolBuilders/** - .git/** - .idea/** - **/.settings/** - **/.* - **/*.lst - **/*.json - **/*font*awesome* - **/banner.txt - **/target/** - + + **/nbactions.xml + **/nb-configuration.xml + **/META-INF/cxf/** + **/META-INF/services/** + **/META-INF/MANIFEST.MF + **/*.csv + **/archetype-resources/** + **/adminlte* + **/dataTables.bootstrap5.min.css + **/goal.txt + **/rat.txt + **/build-copy-javadoc-files.xml + **/maven-eclipse.xml + **/*.iml + **/*.log + **/.externalToolBuilders/** + .git/** + .idea/** + **/.settings/** + **/.* + **/*.lst + **/*.json + **/*font*awesome* + **/banner.txt + **/target/** +