From 84fdaae2b5c651246621ddd5304b8e021d81f831 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 3 Feb 2021 16:52:05 +0100 Subject: [PATCH] bug4811: completed first untested implementation of PermissionChangeListeners --- .../sse/security/shared/BasicUserStore.java | 6 + .../interfaces/AccessControlStore.java | 8 + .../sse/security/interfaces/UserStore.java | 2 +- .../mongodb/AccessControlStoreImpl.java | 6 + .../userstore/mongodb/UserStoreImpl.java | 7 + .../com/sap/sse/security/SecurityService.java | 6 + .../impl/PermissionChangeListeners.java | 239 +++++++++++++----- .../security/impl/SecurityServiceImpl.java | 11 +- 8 files changed, 217 insertions(+), 68 deletions(-) diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/BasicUserStore.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/BasicUserStore.java index e3d122c1d2d..cefbfcf9567 100755 --- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/BasicUserStore.java +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/BasicUserStore.java @@ -28,6 +28,12 @@ public interface BasicUserStore extends UserGroupProvider, Named { UserGroup getUserGroupByName(String name); + /** + * Obtains all user groups that have {@code roleDefinition} as key in their {@link UserGroup#getRoleDefinitionMap() role definition map}, + * regardless of whether for all users or only the members of the group. + */ + Iterable getUserGroupsWithRoleDefinition(RoleDefinition roleDefinition); + UserGroup createUserGroup(UUID groupId, String name) throws UserGroupManagementException; void addUserGroup(UserGroup group) throws UserGroupManagementException; diff --git a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/AccessControlStore.java b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/AccessControlStore.java index 30c2bf6eacf..fb7d0d008b2 100644 --- a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/AccessControlStore.java +++ b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/AccessControlStore.java @@ -82,4 +82,12 @@ public interface AccessControlStore extends Named { * {@code group}. */ Map> getAccessControlListsWithDenials(String typeIdentifier); + + /** + * Obtains all ownership annotations whose {@link Ownership#getTenantOwner() group owner} equals the + * {@code owningUserGroup} passed. + * + * @return an unmodifiable, always valid but potentially empty collection + */ + Iterable getOwnerhipsWithGroupOwner(UserGroup owningUserGroup); } \ No newline at end of file diff --git a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java index f224a0754c1..3f54ad88b6d 100644 --- a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java +++ b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java @@ -115,7 +115,7 @@ public interface UserStore extends BasicUserStore { void removeAllQualifiedRolesForUser(User user); RoleDefinition getRoleDefinitionByPrototype(RolePrototype rolePrototype); - + void deleteUserGroup(UserGroup userGroup) throws UserGroupManagementException; void setDefaultTennantForUserAndUpdate(User user, UserGroup newDefaultTenant, String serverName); diff --git a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/AccessControlStoreImpl.java b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/AccessControlStoreImpl.java index 11da29217a3..e5a85db5698 100644 --- a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/AccessControlStoreImpl.java +++ b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/AccessControlStoreImpl.java @@ -386,6 +386,12 @@ public class AccessControlStoreImpl implements AccessControlStore { } } } + + @Override + public Iterable getOwnerhipsWithGroupOwner(UserGroup owningUserGroup) { + final Set ownerships = userGroupToOwnership.get(owningUserGroup); + return ownerships==null ? Collections.emptySet() : Collections.unmodifiableCollection(ownerships); + } @Override public OwnershipAnnotation getOwnership(final QualifiedObjectIdentifier idOfOwnedObjectAsString) { diff --git a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java index 435b8ed8291..0060fbebe5d 100644 --- a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java +++ b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java @@ -706,6 +706,13 @@ public class UserStoreImpl implements UserStore { }); } + @Override + public Iterable getUserGroupsWithRole(RoleDefinition roleDefinition) { + return LockUtil.executeWithReadLockAndResult(userGroupsLock, () -> { + return roleDefinition == null ? null : new HashSet<>(roleDefinitionsToUserGroups.get(roleDefinition)); + }); + } + @Override public UserGroup getUserGroup(UUID id) { return LockUtil.executeWithReadLockAndResult(userGroupsLock, () -> { diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java b/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java index 14569e17778..fa3b16ff285 100644 --- a/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java @@ -27,6 +27,7 @@ import com.sap.sse.security.interfaces.UserImpl; import com.sap.sse.security.interfaces.UserStore; import com.sap.sse.security.operations.SecurityOperation; import com.sap.sse.security.shared.AccessControlListAnnotation; +import com.sap.sse.security.shared.BasicUserStore; import com.sap.sse.security.shared.HasPermissions; import com.sap.sse.security.shared.HasPermissionsProvider; import com.sap.sse.security.shared.HasPermissions.DefaultActions; @@ -126,6 +127,11 @@ public interface SecurityService extends ReplicableWithObjectInputStream getUserGroupList(); + /** + * @see BasicUserStore#getUserGroupsWithRoleDefinition(RoleDefinition) + */ + Iterable getUserGroupsWithRoleDefinition(RoleDefinition roleDefinition); + UserGroup getUserGroup(UUID id); UserGroup getUserGroupByName(String name); diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/impl/PermissionChangeListeners.java b/java/com.sap.sse.security/src/com/sap/sse/security/impl/PermissionChangeListeners.java index 96ab2e8c64f..70f9d8d15c1 100644 --- a/java/com.sap.sse.security/src/com/sap/sse/security/impl/PermissionChangeListeners.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/impl/PermissionChangeListeners.java @@ -1,6 +1,9 @@ package com.sap.sse.security.impl; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -10,9 +13,11 @@ import com.sap.sse.concurrent.LockUtil; import com.sap.sse.concurrent.NamedReentrantReadWriteLock; import com.sap.sse.security.PermissionChangeListener; import com.sap.sse.security.SecurityService; +import com.sap.sse.security.shared.AccessControlListAnnotation; import com.sap.sse.security.shared.HasPermissions; import com.sap.sse.security.shared.HasPermissions.Action; import com.sap.sse.security.shared.OwnershipAnnotation; +import com.sap.sse.security.shared.PermissionChecker; import com.sap.sse.security.shared.QualifiedObjectIdentifier; import com.sap.sse.security.shared.RoleDefinition; import com.sap.sse.security.shared.WildcardPermission; @@ -75,7 +80,7 @@ public class PermissionChangeListeners { void addOrRemove(QualifiedObjectIdentifier oid, String action, PermissionChangeListener listener); } - public PermissionChangeListeners(final SecurityService securityService) { + PermissionChangeListeners(final SecurityService securityService) { this.securityService = securityService; lock = new NamedReentrantReadWriteLock(PermissionChangeListeners.class.getSimpleName(), /* fair */ false); permissionChangeListenersByType = new ConcurrentHashMap<>(); @@ -89,11 +94,11 @@ public class PermissionChangeListeners { * behaves as if it had been invoked for each expanded single permission with a single type and a single * action and a single object ID. At least one type and object ID must be provided. */ - public void addPermissionChangeListener(WildcardPermission permission, PermissionChangeListener listener) { + void addPermissionChangeListener(WildcardPermission permission, PermissionChangeListener listener) { addOrRemovePermissionChangeListener(permission, listener, this::addPermissionChangeListenerInternal); } - public void removePermissionChangeListener(WildcardPermission permission, PermissionChangeListener listener) { + void removePermissionChangeListener(WildcardPermission permission, PermissionChangeListener listener) { addOrRemovePermissionChangeListener(permission, listener, this::removePermissionChangeListenerInternal); } @@ -168,34 +173,26 @@ public class PermissionChangeListeners { } /** - * Analyzes a change of permission/role on user: this happens on a specific {@code user}, and we can check if the - * permission added/removed implies any permission for which a listener registered; this does still not necessarily - * imply a change (the user could, e.g., have obtained the permission in more than one way), but this kind of change - * will usually happen at very low frequency and in almost all cases will effectively cause a change. If the role is - * qualified (user/group), check the qualification against the ownerships of the objects identified by the - * permissions for which we have listener registrations. + * Analyzes adding/removing of role on a user: we can check if the role added/removed implies any permission for + * which a listener registered; this does still not necessarily imply a change (the user could, e.g., have obtained + * the permission in more than one way), but this kind of change will usually happen at very low frequency and in + * almost all cases will effectively cause a change. If the role is qualified (user/group), check the qualification + * against the ownerships of the objects identified by the permissions for which we have listener registrations. */ - public void roleAddedToOrRemovedFromUser(User user, Role role) { + void roleAddedToOrRemovedFromUser(User user, Role role) { LockUtil.executeWithReadLock(lock, () -> { outer: for (final WildcardPermission permission : role.getPermissions()) { // filter by type if provided; for type wildcard, all listener registrations will have to be scanned - final Iterable typesToScan; - if (!permission.getParts().isEmpty()) { - if (permission.getParts().get(0).contains(WildcardPermission.WILDCARD_TOKEN)) { - typesToScan = permissionChangeListenersByType.keySet(); - } else { - typesToScan = permission.getParts().get(0); - } - for (final String typeName : typesToScan) { - for (final Entry> permissionAndListener : permissionChangeListenersByType.get(typeName).entrySet()) { - // all listener registrations' permissions have been expanded into single permissions upon registration: - final QualifiedObjectIdentifier objectId = permissionAndListener.getKey().getQualifiedObjectIdentifiers().iterator().next(); - final OwnershipAnnotation ownershipAnnotation = securityService.getOwnership(objectId); - if (ownershipAnnotation == null || matchesQualification(ownershipAnnotation.getAnnotation(), role.getQualifiedForTenant(), role.getQualifiedForUser())) { - if (permission.implies(permissionAndListener.getKey())) { - notifyListeners(permissionAndListener.getKey(), permissionAndListener.getValue().keySet()); - break outer; - } + final Iterable typesToScan = getTypesToScan(permission); + for (final String typeName : typesToScan) { + for (final Entry> permissionAndListener : permissionChangeListenersByType.get(typeName).entrySet()) { + // all listener registrations' permissions have been expanded into single permissions upon registration: + final QualifiedObjectIdentifier objectId = permissionAndListener.getKey().getQualifiedObjectIdentifiers().iterator().next(); + final OwnershipAnnotation ownershipAnnotation = securityService.getOwnership(objectId); + if (ownershipAnnotation == null || matchesQualification(ownershipAnnotation.getAnnotation(), role.getQualifiedForTenant(), role.getQualifiedForUser())) { + if (permission.implies(permissionAndListener.getKey())) { + notifyListeners(permissionAndListener.getKey(), permissionAndListener.getValue().keySet()); + break outer; } } } @@ -204,6 +201,22 @@ public class PermissionChangeListeners { }); } + /** + * From a permission that may contain wildcards for the type (first part) finds all type names from + * {@link #permissionChangeListenersByType}'s key set that match. + */ + private Iterable getTypesToScan(final WildcardPermission permission) { + final Iterable typesToScan; + if (permission.getParts().isEmpty()) { + typesToScan = Collections.emptySet(); + } else if (permission.getParts().get(0).contains(WildcardPermission.WILDCARD_TOKEN)) { + typesToScan = permissionChangeListenersByType.keySet(); + } else { + typesToScan = Util.filter(permission.getParts().get(0), typeName->permissionChangeListenersByType.containsKey(typeName)); + } + return typesToScan; + } + private void notifyListeners(WildcardPermission permission, Iterable listeners) { final Iterable usersWithPermissions = securityService.getUsersWithPermissions(permission); for (final PermissionChangeListener listener : listeners) { @@ -216,55 +229,151 @@ public class PermissionChangeListeners { && (qualifiedForUser == null || Util.equalsWithNull(qualifiedForUser, ownership.getUserOwner())); } - public void permissionAddedToOrRemovedFromUser(User user, WildcardPermission permission) { - // TODO change permission/role on user: this happens on a specific user, and we can if the permission - // added/removed implies the permission in question; this does still not necessarily imply a change (the user - // could, e.g., have obtained the permission in more than one way), but this kind of change will usually happen - // at very low frequency and in almost all cases will effectively cause a change + /** + * Analyzes addition/removal of a permission on a user: we can check if the permission added/removed implies any + * permission for which a listener registered; this does still not necessarily imply a change (the user could, e.g., + * have obtained the permission in more than one way), but this kind of change will usually happen at very low + * frequency and in almost all cases will effectively cause a change. + */ + void permissionAddedToOrRemovedFromUser(User user, WildcardPermission permission) { + notifyListenersForPermissionsImplied(permission); } - public void userAddedToOrRemovedFromGroup(User user, UserGroup group) { - // TODO group membership changes only need to be considered in case of the group owning the object in question; - // filtered further to only those groups granting roles to its members which imply the permission in question - // results in a high accuracy trigger + private void notifyListenersForPermissionsImplied(WildcardPermission permission) { + LockUtil.executeWithReadLock(lock, () -> { + // filter by type if provided; for type wildcard, all listener registrations will have to be scanned + outer: for (final String typeName : getTypesToScan(permission)) { + for (final Entry> permissionAndListener : permissionChangeListenersByType.get(typeName).entrySet()) { + // all listener registrations' permissions have been expanded into single permissions upon registration: + if (permission.implies(permissionAndListener.getKey())) { + notifyListeners(permissionAndListener.getKey(), permissionAndListener.getValue().keySet()); + break outer; + } + } + } + }); } - public void aclChanged(QualifiedObjectIdentifier objectId) { - // TODO ACL changes only need to be considered on the object itself and can further be filtered by the specific - // permission in question; they hardly ever occur + /** + * Group membership changes only need to be considered in case of the group owning the object for which we have + * permission change listeners. We can filter further to only those groups granting roles to its members which imply the + * permission in question, which results in a high accuracy trigger. + */ + void userAddedToOrRemovedFromGroup(User user, UserGroup group) { + LockUtil.executeWithReadLock(lock, () -> { + for (final Entry>> objectIdAndListeners : permissionChangeListenersByObject.entrySet()) { + final OwnershipAnnotation ownershipAnnotation = securityService.getOwnership(objectIdAndListeners.getKey()); + if (ownershipAnnotation != null && Util.equalsWithNull(ownershipAnnotation.getAnnotation().getTenantOwner(), group)) { + // group ownership matches; check which permissions for which a listener is subscribed is implied by any of the group's roles: + Util.filter(objectIdAndListeners.getValue().entrySet(), permissionAndListeners->isImpliedByGroupRoleForMembersOrAll(permissionAndListeners.getKey(), group)) + .forEach(pAndL->notifyListeners(pAndL.getKey(), pAndL.getValue().keySet())); + } + } + }); + } + + /** + * Tells if the {@code permission} is implied by any of those roles from {@code group} that apply only to group + * members. + */ + private boolean isImpliedByGroupRoleForMembersOrAll(WildcardPermission permission, UserGroup group) { + return !Util.isEmpty(Util.filter(group.getRoleDefinitionMap().entrySet(), + roleAndForAll-> + !roleAndForAll.getValue() && + !Util.isEmpty(Util.filter(roleAndForAll.getKey().getPermissions(), p->p.implies(permission))))); + } + + /** + * ACL changes only need to be considered on the object itself and can further be filtered by the specific + * permission in question; they hardly ever occur + */ + void aclChanged(QualifiedObjectIdentifier objectId) { + LockUtil.executeWithReadLock(lock, () -> { + for (final Entry> listenersForObject : permissionChangeListenersByObject.get(objectId).entrySet()) { + notifyListeners(listenersForObject.getKey(), listenersForObject.getValue().keySet()); + } + }); } void permissionAddedToOrRemovedFromRoleDefinition(RoleDefinition roleDefinition, Iterable oldPermissions, Iterable newPermissions) { - // TODO adding the permission to or removing it from a role is easy to detect because we can check whether the - // permission(s) added/removed imply the permission in question; permissions implied by roles are not changed - // frequently - // TODO we need to find the users who have a role based on roleDefinition assigned if the permission - // added/removed implies a permission that has listeners here - } - - public void roleAddedToOrRemovedFromGroup(UserGroup group, RoleDefinition roleDefinition) { - // TODO adding/removing roles a group grants is only relevant when the group owns the object in question and the - // role(s) imply the permission in question; both are efficient (O(1)) to determine - } - - public void roleDefinitionRemoved(RoleDefinition roleDefinition) { - // TODO - } - - public void userGroupDeleted(UserGroup group) { - // TODO - } - - public void ownershipChanged(QualifiedObjectIdentifier objectId) { - // TODO ownership changes are only relevant on the object in question and hence are easy to observe; ownership - // changes don't occur often, especially not for the object type (LANDSCAPE) key to our considerations here + final Iterable permissionsAdded = Util.filter(newPermissions, newPermission->!Util.contains(oldPermissions, newPermission)); + final Iterable permissionsRemoved = Util.filter(oldPermissions, oldPermission->!Util.contains(newPermissions, oldPermission)); + for (final WildcardPermission permissionAdded : permissionsAdded) { + notifyListenersForPermissionsImplied(permissionAdded); + } + for (final WildcardPermission permissionRemoved : permissionsRemoved) { + notifyListenersForPermissionsImplied(permissionRemoved); + } } /** - * Call before the user and any of its permission or role assignments or ownerships has been cleared! + * adding/removing roles that a group grants is only relevant when the group owns the object in question and the + * role added/removed implies the permission in question; both can be determined efficiently */ - public void userDeleted(User user) { - // TODO find out if the user had any of the key permissions and notify the corresponding listeners + void roleAddedToOrRemovedFromGroup(UserGroup group, RoleDefinition roleDefinition) { + LockUtil.executeWithReadLock(lock, () -> { + for (final Entry>> listenersForObject : permissionChangeListenersByObject.entrySet()) { + final OwnershipAnnotation ownershipAnnotation = securityService.getOwnership(listenersForObject.getKey()); + if (ownershipAnnotation != null && Util.equalsWithNull(ownershipAnnotation.getAnnotation().getTenantOwner(), group)) { + // so we have listener registrations for an object owned by the group; filter permissions to those implied by roleDefinition + for (final Entry> e : listenersForObject.getValue().entrySet()) { + if (!Util.isEmpty(Util.filter(roleDefinition.getPermissions(), p->p.implies(e.getKey())))) { + notifyListeners(e.getKey(), e.getValue().keySet()); + } + } + } + } + }); + } + + /** + * Notifies all permission change listeners that are registered for any permission that is implied by any of + * {@code roleDefinition}'s {@link RoleDefinition#getPermissions() permissions}. + */ + void roleDefinitionRemoved(RoleDefinition roleDefinition) { + for (final WildcardPermission permission : roleDefinition.getPermissions()) { + notifyListenersForPermissionsImplied(permission); + } + } + + /** + * ownership changes are only relevant on the object in question and hence are easy to observe; ownership + * changes don't occur often, especially not for the object type (LANDSCAPE) key to our considerations here + */ + void ownershipChanged(QualifiedObjectIdentifier objectId) { + LockUtil.executeWithReadLock(lock, () -> { + final ConcurrentHashMap> listenersForObject = permissionChangeListenersByObject.get(objectId); + if (listenersForObject != null) { + for (final Entry> e : listenersForObject.entrySet()) { + notifyListeners(e.getKey(), e.getValue().keySet()); + } + } + }); + } + + /** + * Call before the user and any of its permission or role assignments or ownerships has been cleared!

+ * + * Finds out if the user had any of the key permissions and notifies the corresponding listeners + */ + void userDeleted(User user) { + final Map> listenersToNotify = new HashMap<>(); + LockUtil.executeWithReadLock(lock, () -> { + final User allUser = securityService.getAllUser(); + for (final Entry>> permissionsAndListeners : permissionChangeListenersByObject.entrySet()) { + final OwnershipAnnotation ownershipAnnotation = securityService.getOwnership(permissionsAndListeners.getKey()); + final AccessControlListAnnotation aclAnnotation = securityService.getAccessControlList(permissionsAndListeners.getKey()); + for (final Entry> permissionAndListeners : permissionsAndListeners.getValue().entrySet()) { + if (PermissionChecker.isPermitted( + permissionAndListeners.getKey(), user, allUser, + ownershipAnnotation==null?null:ownershipAnnotation.getAnnotation(), + aclAnnotation==null?null:aclAnnotation.getAnnotation())) { + permissionAndListeners.getValue().keySet().forEach(listener->Util.addToValueSet(listenersToNotify, permissionAndListeners.getKey(), listener)); + } + } + } + }); + listenersToNotify.forEach((permission, listeners)->notifyListeners(permission, listeners)); } } diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java b/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java index 7d69e129f28..ba637a45481 100755 --- a/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java @@ -715,6 +715,11 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat return store.getUserGroups(); } + @Override + public Iterable getUserGroupsWithRoleDefinition(RoleDefinition roleDefinition) { + return store.getUserGroupsWithRoleDefinition(roleDefinition); + } + @Override public UserGroup getUserGroup(UUID id) { return store.getUserGroup(id); @@ -838,7 +843,9 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat if (userGroup == null) { logger.warning("Strange: the user group with ID "+groupId+" which is about to be deleted couldn't be found"); } else { - permissionChangeListeners.userGroupDeleted(userGroup); + for (final OwnershipAnnotation ownershipWithGroupAsOwner : accessControlStore.getOwnerhipsWithGroupOwner(userGroup)) { + permissionChangeListeners.ownershipChanged(ownershipWithGroupAsOwner.getIdOfAnnotatedObject()); + } accessControlStore.removeAllOwnershipsFor(userGroup); store.deleteUserGroup(userGroup); } @@ -898,7 +905,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat final ScheduledExecutorService foregroundExecutor = ThreadPoolUtil.INSTANCE.getDefaultForegroundTaskThreadPoolExecutor(); final int numberOfJobs = ThreadPoolUtil.INSTANCE.getReasonableThreadPoolSize(); final ConcurrentMap result = new ConcurrentHashMap<>(); - final User allUser = store.getUserByName(SecurityService.ALL_USERNAME); + final User allUser = getAllUser(); final ConcurrentLinkedDeque userList = new ConcurrentLinkedDeque<>(); Util.addAll(getUserList(), userList); final Set> futures = new HashSet<>();