From ea49ff7dcb39150221dafac1bff8e352cd4d49e7 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Tue, 7 May 2024 11:02:59 -0400 Subject: [PATCH] [AC-1121] Update authorization for orphaned collections (#4047) * update BulkCollectionAuthorizationHandler to account for orphaned collections --- .../BulkCollectionAuthorizationHandler.cs | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs b/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs index c75e529b6..497c8b9a1 100644 --- a/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs +++ b/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs @@ -25,6 +25,8 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler? _managedCollectionsIds; + private HashSet? _orphanedCollectionsIds; + public BulkCollectionAuthorizationHandler( ICurrentContext currentContext, ICollectionRepository collectionRepository, @@ -140,7 +142,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler CanManageCollectionsAsync(ICollection targetCollections) + private async Task CanManageCollectionsAsync(ICollection targetCollections, + CurrentContextOrganization? org) { if (_managedCollectionsIds == null) { var allUserCollections = await _collectionRepository .GetManyByUserIdAsync(_currentContext.UserId!.Value, useFlexibleCollections: true); - _managedCollectionsIds = allUserCollections + + var managedCollectionIds = allUserCollections .Where(c => c.Manage) - .Select(c => c.Id) + .Select(c => c.Id); + + _managedCollectionsIds = managedCollectionIds .ToHashSet(); } - return targetCollections.All(tc => _managedCollectionsIds.Contains(tc.Id)); + var canManageTargetCollections = targetCollections.All(tc => _managedCollectionsIds.Contains(tc.Id)); + + // The user can manage all target collections, stop here, return true. + if (canManageTargetCollections) + { + return true; + } + + // The user is not assigned to manage all target collections + // If the user is an Owner/Admin/Custom user with edit, check if any targets are orphaned collections + if (org is not ({ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or { Permissions.EditAnyCollection: true })) + { + // User is not allowed to manage orphaned collections + return false; + } + + if (_orphanedCollectionsIds == null) + { + var orgCollections = await _collectionRepository.GetManyByOrganizationIdWithAccessAsync(_targetOrganizationId); + + // Orphaned collections are collections that have no users or groups with manage permissions + _orphanedCollectionsIds = orgCollections.Where(c => + !c.Item2.Users.Any(u => u.Manage) && !c.Item2.Groups.Any(g => g.Manage)) + .Select(c => c.Item1.Id) + .ToHashSet(); + } + + return targetCollections.All(tc => _orphanedCollectionsIds.Contains(tc.Id) || _managedCollectionsIds.Contains(tc.Id)); } private async Task GetOrganizationAbilityAsync(CurrentContextOrganization? organization)