From 021e69bc5dfea8be3b74f7a046a1cd48a206a712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Tue, 10 Jun 2025 09:57:29 +0100 Subject: [PATCH] [PM-17154] Limit item deletion feature flag logic removal (#5925) * Refactor CiphersController and related tests by removing unused IFeatureService dependency and associated feature flag checks. Cleaned up tests to reflect these changes, ensuring they focus on manage permissions without reliance on feature flags. * Refactor CipherService and related tests by removing feature flag checks for item deletion permissions. Updated tests to focus on user manage permissions without reliance on feature flags, ensuring cleaner and more maintainable code. * Enhance CiphersControllerTests by adding user retrieval and organization ability checks. Updated test cases to ensure proper handling of item deletion permissions based on user roles and organization settings, improving test coverage and reliability. --- .../Vault/Controllers/CiphersController.cs | 8 - .../Services/Implementations/CipherService.cs | 16 - .../Controllers/CiphersControllerTests.cs | 621 +++++++----------- .../Vault/Services/CipherServiceTests.cs | 497 +++----------- 4 files changed, 322 insertions(+), 820 deletions(-) diff --git a/src/Api/Vault/Controllers/CiphersController.cs b/src/Api/Vault/Controllers/CiphersController.cs index 7b302f3724..5991d0babb 100644 --- a/src/Api/Vault/Controllers/CiphersController.cs +++ b/src/Api/Vault/Controllers/CiphersController.cs @@ -42,7 +42,6 @@ public class CiphersController : Controller private readonly ICurrentContext _currentContext; private readonly ILogger _logger; private readonly GlobalSettings _globalSettings; - private readonly IFeatureService _featureService; private readonly IOrganizationCiphersQuery _organizationCiphersQuery; private readonly IApplicationCacheService _applicationCacheService; private readonly ICollectionRepository _collectionRepository; @@ -57,7 +56,6 @@ public class CiphersController : Controller ICurrentContext currentContext, ILogger logger, GlobalSettings globalSettings, - IFeatureService featureService, IOrganizationCiphersQuery organizationCiphersQuery, IApplicationCacheService applicationCacheService, ICollectionRepository collectionRepository) @@ -71,7 +69,6 @@ public class CiphersController : Controller _currentContext = currentContext; _logger = logger; _globalSettings = globalSettings; - _featureService = featureService; _organizationCiphersQuery = organizationCiphersQuery; _applicationCacheService = applicationCacheService; _collectionRepository = collectionRepository; @@ -375,11 +372,6 @@ public class CiphersController : Controller private async Task CanDeleteOrRestoreCipherAsAdminAsync(Guid organizationId, IEnumerable cipherIds) { - if (!_featureService.IsEnabled(FeatureFlagKeys.LimitItemDeletion)) - { - return await CanEditCipherAsAdminAsync(organizationId, cipherIds); - } - var org = _currentContext.GetOrganization(organizationId); // If we're not an "admin" or if we're a provider user we don't need to check the ciphers diff --git a/src/Core/Vault/Services/Implementations/CipherService.cs b/src/Core/Vault/Services/Implementations/CipherService.cs index 413aee3e0d..5d17441024 100644 --- a/src/Core/Vault/Services/Implementations/CipherService.cs +++ b/src/Core/Vault/Services/Implementations/CipherService.cs @@ -821,11 +821,6 @@ public class CipherService : ICipherService private async Task UserCanDeleteAsync(CipherDetails cipher, Guid userId) { - if (!_featureService.IsEnabled(FeatureFlagKeys.LimitItemDeletion)) - { - return await UserCanEditAsync(cipher, userId); - } - var user = await _userService.GetUserByIdAsync(userId); var organizationAbility = cipher.OrganizationId.HasValue ? await _applicationCacheService.GetOrganizationAbilityAsync(cipher.OrganizationId.Value) : null; @@ -835,11 +830,6 @@ public class CipherService : ICipherService private async Task UserCanRestoreAsync(CipherDetails cipher, Guid userId) { - if (!_featureService.IsEnabled(FeatureFlagKeys.LimitItemDeletion)) - { - return await UserCanEditAsync(cipher, userId); - } - var user = await _userService.GetUserByIdAsync(userId); var organizationAbility = cipher.OrganizationId.HasValue ? await _applicationCacheService.GetOrganizationAbilityAsync(cipher.OrganizationId.Value) : null; @@ -1059,17 +1049,11 @@ public class CipherService : ICipherService } // This method is used to filter ciphers based on the user's permissions to delete them. - // It supports both the old and new logic depending on the feature flag. private async Task> FilterCiphersByDeletePermission( IEnumerable ciphers, HashSet cipherIdsSet, Guid userId) where T : CipherDetails { - if (!_featureService.IsEnabled(FeatureFlagKeys.LimitItemDeletion)) - { - return ciphers.Where(c => cipherIdsSet.Contains(c.Id) && c.Edit).ToList(); - } - var user = await _userService.GetUserByIdAsync(userId); var organizationAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync(); diff --git a/test/Api.Test/Vault/Controllers/CiphersControllerTests.cs b/test/Api.Test/Vault/Controllers/CiphersControllerTests.cs index bca6bbc048..d1f5a212c9 100644 --- a/test/Api.Test/Vault/Controllers/CiphersControllerTests.cs +++ b/test/Api.Test/Vault/Controllers/CiphersControllerTests.cs @@ -4,7 +4,6 @@ using Bit.Api.Vault.Controllers; using Bit.Api.Vault.Models; using Bit.Api.Vault.Models.Request; using Bit.Api.Vault.Models.Response; -using Bit.Core; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; @@ -169,6 +168,7 @@ public class CiphersControllerTests } sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); @@ -197,65 +197,7 @@ public class CiphersControllerTests [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task DeleteAdmin_WithOwnerOrAdmin_WithEditPermission_DeletesCipher( - OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, - CurrentContextOrganization organization, SutProvider sutProvider) - { - cipherDetails.UserId = null; - cipherDetails.OrganizationId = organization.Id; - cipherDetails.Edit = true; - cipherDetails.Manage = false; - - organization.Type = organizationUserType; - - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(new List - { - cipherDetails - }); - - await sutProvider.Sut.DeleteAdmin(cipherDetails.Id); - - await sutProvider.GetDependency().Received(1).DeleteAsync(cipherDetails, userId, true); - } - - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task DeleteAdmin_WithOwnerOrAdmin_WithoutEditPermission_ThrowsNotFoundException( - OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, - CurrentContextOrganization organization, SutProvider sutProvider) - { - cipherDetails.UserId = null; - cipherDetails.OrganizationId = organization.Id; - cipherDetails.Edit = false; - cipherDetails.Manage = false; - - organization.Type = organizationUserType; - - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(new List - { - cipherDetails - }); - - await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteAdmin(cipherDetails.Id)); - - await sutProvider.GetDependency().DidNotReceive().DeleteAsync(Arg.Any(), Arg.Any(), Arg.Any()); - } - - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task DeleteAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithManagePermission_DeletesCipher( + public async Task DeleteAdmin_WithOwnerOrAdmin_WithManagePermission_DeletesCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -266,7 +208,6 @@ public class CiphersControllerTests organization.Type = organizationUserType; - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -293,7 +234,7 @@ public class CiphersControllerTests [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task DeleteAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( + public async Task DeleteAdmin_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -304,7 +245,6 @@ public class CiphersControllerTests organization.Type = organizationUserType; - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -339,11 +279,22 @@ public class CiphersControllerTests organization.Type = organizationUserType; sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) - .Returns(new List { new() { Id = cipherDetails.Id } }); + .Returns(new List + { + new() { Id = cipherDetails.Id, OrganizationId = cipherDetails.OrganizationId } + }); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organization.Id) + .Returns(new OrganizationAbility + { + Id = organization.Id, + LimitItemDeletion = true + }); await sutProvider.Sut.DeleteAdmin(cipherDetails.Id); @@ -426,10 +377,14 @@ public class CiphersControllerTests await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteAdmin(cipher.Id)); } + + + + [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task DeleteManyAdmin_WithOwnerOrAdmin_WithEditPermission_DeletesCiphers( + public async Task DeleteManyAdmin_WithOwnerOrAdmin_WithManagePermission_DeletesCiphers( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List ciphers, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -437,74 +392,6 @@ public class CiphersControllerTests model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(ciphers.Select(c => new CipherDetails - { - Id = c.Id, - OrganizationId = organization.Id, - Edit = true - }).ToList()); - - await sutProvider.Sut.DeleteManyAdmin(model); - - await sutProvider.GetDependency() - .Received(1) - .DeleteManyAsync( - Arg.Is>(ids => - ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()), - userId, organization.Id, true); - } - - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task DeleteManyAdmin_WithOwnerOrAdmin_WithoutEditPermission_ThrowsNotFoundException( - OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List ciphers, - CurrentContextOrganization organization, SutProvider sutProvider) - { - model.OrganizationId = organization.Id.ToString(); - model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); - - organization.Type = organizationUserType; - - sutProvider.GetDependency() - .GetProperUserId(default) - .ReturnsForAnyArgs(userId); - - sutProvider.GetDependency() - .GetOrganization(new Guid(model.OrganizationId)) - .Returns(organization); - - sutProvider.GetDependency() - .GetManyByOrganizationIdAsync(new Guid(model.OrganizationId)) - .Returns(ciphers); - - sutProvider.GetDependency() - .GetOrganizationAbilityAsync(new Guid(model.OrganizationId)) - .Returns(new OrganizationAbility - { - Id = new Guid(model.OrganizationId), - AllowAdminAccessToAllCollectionItems = false, - }); - - await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteManyAdmin(model)); - } - - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task DeleteManyAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithManagePermission_DeletesCiphers( - OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List ciphers, - CurrentContextOrganization organization, SutProvider sutProvider) - { - model.OrganizationId = organization.Id.ToString(); - model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); - organization.Type = organizationUserType; - - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -540,7 +427,7 @@ public class CiphersControllerTests [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task DeleteManyAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( + public async Task DeleteManyAdmin_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List ciphers, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -548,7 +435,6 @@ public class CiphersControllerTests model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -586,10 +472,18 @@ public class CiphersControllerTests organization.Type = organizationUserType; sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) - .Returns(ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id }).ToList()); + .Returns(ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id, OrganizationId = organization.Id }).ToList()); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organization.Id) + .Returns(new OrganizationAbility + { + Id = organization.Id, + LimitItemDeletion = true + }); await sutProvider.Sut.DeleteManyAdmin(model); @@ -688,67 +582,14 @@ public class CiphersControllerTests await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteManyAdmin(model)); } - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithEditPermission_SoftDeletesCipher( - OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, - CurrentContextOrganization organization, SutProvider sutProvider) - { - cipherDetails.UserId = null; - cipherDetails.OrganizationId = organization.Id; - cipherDetails.Edit = true; - organization.Type = organizationUserType; - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(new List - { - cipherDetails - }); - await sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id); - - await sutProvider.GetDependency().Received(1).SoftDeleteAsync(cipherDetails, userId, true); - } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithoutEditPermission_ThrowsNotFoundException( - OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, - CurrentContextOrganization organization, SutProvider sutProvider) - { - cipherDetails.UserId = null; - cipherDetails.OrganizationId = organization.Id; - cipherDetails.Edit = false; - cipherDetails.Manage = false; - - organization.Type = organizationUserType; - - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(new List - { - cipherDetails - }); - - await Assert.ThrowsAsync(() => sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id)); - - await sutProvider.GetDependency().DidNotReceive().SoftDeleteAsync(Arg.Any(), Arg.Any(), Arg.Any()); - } - - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task PutDeleteAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithManagePermission_SoftDeletesCipher( + public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithManagePermission_SoftDeletesCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -759,7 +600,6 @@ public class CiphersControllerTests organization.Type = organizationUserType; - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -786,7 +626,7 @@ public class CiphersControllerTests [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task PutDeleteAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( + public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -797,7 +637,6 @@ public class CiphersControllerTests organization.Type = organizationUserType; - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -833,12 +672,20 @@ public class CiphersControllerTests organization.Type = organizationUserType; sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) - .Returns(new List { new() { Id = cipherDetails.Id } }); + .Returns(new List { new() { Id = cipherDetails.Id, OrganizationId = organization.Id } }); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organization.Id) + .Returns(new OrganizationAbility + { + Id = organization.Id, + LimitItemDeletion = true + }); await sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id); @@ -856,6 +703,7 @@ public class CiphersControllerTests organization.Type = organizationUserType; sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency().GetManyByOrganizationIdAsync(organization.Id).Returns(new List { cipherDetails }); @@ -890,6 +738,70 @@ public class CiphersControllerTests await sutProvider.GetDependency().Received(1).SoftDeleteAsync(cipherDetails, userId, true); } + [Theory] + [BitAutoData(OrganizationUserType.Owner)] + [BitAutoData(OrganizationUserType.Admin)] + public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithEditPermission_WithLimitItemDeletionFalse_SoftDeletesCipher( + OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, + CurrentContextOrganization organization, SutProvider sutProvider) + { + cipherDetails.UserId = null; + cipherDetails.OrganizationId = organization.Id; + cipherDetails.Edit = true; + cipherDetails.Manage = false; // Only Edit permission, not Manage + organization.Type = organizationUserType; + + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); + sutProvider.GetDependency() + .GetManyByUserIdAsync(userId) + .Returns(new List { cipherDetails }); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organization.Id) + .Returns(new OrganizationAbility + { + Id = organization.Id, + LimitItemDeletion = false + }); + + await sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id); + + await sutProvider.GetDependency().Received(1).SoftDeleteAsync(cipherDetails, userId, true); + } + + [Theory] + [BitAutoData(OrganizationUserType.Owner)] + [BitAutoData(OrganizationUserType.Admin)] + public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithEditPermission_WithLimitItemDeletionTrue_ThrowsNotFoundException( + OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, + CurrentContextOrganization organization, SutProvider sutProvider) + { + cipherDetails.UserId = null; + cipherDetails.OrganizationId = organization.Id; + cipherDetails.Edit = true; + cipherDetails.Manage = false; // Only Edit permission, not Manage + organization.Type = organizationUserType; + + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); + sutProvider.GetDependency() + .GetManyByUserIdAsync(userId) + .Returns(new List { cipherDetails }); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organization.Id) + .Returns(new OrganizationAbility + { + Id = organization.Id, + LimitItemDeletion = true + }); + + await Assert.ThrowsAsync(() => sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id)); + } + [Theory] [BitAutoData] public async Task PutDeleteAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException( @@ -922,10 +834,14 @@ public class CiphersControllerTests await Assert.ThrowsAsync(() => sutProvider.Sut.PutDeleteAdmin(cipher.Id)); } + + + + [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task PutDeleteManyAdmin_WithOwnerOrAdmin_WithEditPermission_SoftDeletesCiphers( + public async Task PutDeleteManyAdmin_WithOwnerOrAdmin_WithManagePermission_SoftDeletesCiphers( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List ciphers, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -933,65 +849,6 @@ public class CiphersControllerTests model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(ciphers.Select(c => new CipherDetails - { - Id = c.Id, - OrganizationId = organization.Id, - Edit = true - }).ToList()); - - await sutProvider.Sut.PutDeleteManyAdmin(model); - - await sutProvider.GetDependency() - .Received(1) - .SoftDeleteManyAsync( - Arg.Is>(ids => - ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()), - userId, organization.Id, true); - } - - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task PutDeleteManyAdmin_WithOwnerOrAdmin_WithoutEditPermission_ThrowsNotFoundException( - OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List ciphers, - CurrentContextOrganization organization, SutProvider sutProvider) - { - model.OrganizationId = organization.Id.ToString(); - model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); - organization.Type = organizationUserType; - - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(ciphers.Select(c => new CipherDetails - { - Id = c.Id, - OrganizationId = organization.Id, - Edit = false - }).ToList()); - - await Assert.ThrowsAsync(() => sutProvider.Sut.PutDeleteManyAdmin(model)); - } - - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task PutDeleteManyAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithManagePermission_SoftDeletesCiphers( - OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List ciphers, - CurrentContextOrganization organization, SutProvider sutProvider) - { - model.OrganizationId = organization.Id.ToString(); - model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); - organization.Type = organizationUserType; - - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -1027,7 +884,7 @@ public class CiphersControllerTests [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task PutDeleteManyAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( + public async Task PutDeleteManyAdmin_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List ciphers, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -1035,7 +892,6 @@ public class CiphersControllerTests model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -1073,10 +929,18 @@ public class CiphersControllerTests organization.Type = organizationUserType; sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) - .Returns(ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id }).ToList()); + .Returns(ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id, OrganizationId = organization.Id }).ToList()); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organization.Id) + .Returns(new OrganizationAbility + { + Id = organization.Id, + LimitItemDeletion = true + }); await sutProvider.Sut.PutDeleteManyAdmin(model); @@ -1099,7 +963,14 @@ public class CiphersControllerTests model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; + // Set organization ID on ciphers to avoid "Cipher needs to belong to a user or an organization" error + foreach (var cipher in ciphers) + { + cipher.OrganizationId = organization.Id; + } + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers); sutProvider.GetDependency().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility @@ -1130,7 +1001,14 @@ public class CiphersControllerTests organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = true; + // Set organization ID on ciphers to avoid "Cipher needs to belong to a user or an organization" error + foreach (var cipher in ciphers) + { + cipher.OrganizationId = organization.Id; + } + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers); @@ -1175,68 +1053,14 @@ public class CiphersControllerTests await Assert.ThrowsAsync(() => sutProvider.Sut.PutDeleteManyAdmin(model)); } - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithEditPermission_RestoresCipher( - OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, - CurrentContextOrganization organization, SutProvider sutProvider) - { - cipherDetails.UserId = null; - cipherDetails.OrganizationId = organization.Id; - cipherDetails.Type = CipherType.Login; - cipherDetails.Data = JsonSerializer.Serialize(new CipherLoginData()); - cipherDetails.Edit = true; - organization.Type = organizationUserType; - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(new List - { - cipherDetails - }); - var result = await sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id); - - Assert.IsType(result); - await sutProvider.GetDependency().Received(1).RestoreAsync(cipherDetails, userId, true); - } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithoutEditPermission_ThrowsNotFoundException( - OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, - CurrentContextOrganization organization, SutProvider sutProvider) - { - cipherDetails.UserId = null; - cipherDetails.OrganizationId = organization.Id; - cipherDetails.Edit = false; - cipherDetails.Manage = false; - - organization.Type = organizationUserType; - - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(new List - { - cipherDetails - }); - - await Assert.ThrowsAsync(() => sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id)); - } - - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task PutRestoreAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithManagePermission_RestoresCipher( + public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithManagePermission_RestoresCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -1249,7 +1073,6 @@ public class CiphersControllerTests organization.Type = organizationUserType; - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -1277,7 +1100,7 @@ public class CiphersControllerTests [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task PutRestoreAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( + public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -1288,7 +1111,6 @@ public class CiphersControllerTests organization.Type = organizationUserType; - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -1323,11 +1145,19 @@ public class CiphersControllerTests organization.Type = organizationUserType; sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) - .Returns(new List { new() { Id = cipherDetails.Id } }); + .Returns(new List { new() { Id = cipherDetails.Id, OrganizationId = organization.Id } }); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organization.Id) + .Returns(new OrganizationAbility + { + Id = organization.Id, + LimitItemDeletion = true + }); var result = await sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id); @@ -1386,6 +1216,75 @@ public class CiphersControllerTests await sutProvider.GetDependency().Received(1).RestoreAsync(cipherDetails, userId, true); } + [Theory] + [BitAutoData(OrganizationUserType.Owner)] + [BitAutoData(OrganizationUserType.Admin)] + public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithEditPermission_LimitItemDeletionFalse_RestoresCipher( + OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, + CurrentContextOrganization organization, SutProvider sutProvider) + { + cipherDetails.UserId = null; + cipherDetails.OrganizationId = organization.Id; + cipherDetails.Type = CipherType.Login; + cipherDetails.Data = JsonSerializer.Serialize(new CipherLoginData()); + cipherDetails.Edit = true; + cipherDetails.Manage = false; // Only Edit permission, not Manage + organization.Type = organizationUserType; + + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); + sutProvider.GetDependency() + .GetManyByUserIdAsync(userId) + .Returns(new List { cipherDetails }); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organization.Id) + .Returns(new OrganizationAbility + { + Id = organization.Id, + LimitItemDeletion = false // Permissive mode - Edit permission should work + }); + + var result = await sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id); + + Assert.IsType(result); + await sutProvider.GetDependency().Received(1).RestoreAsync(cipherDetails, userId, true); + } + + [Theory] + [BitAutoData(OrganizationUserType.Owner)] + [BitAutoData(OrganizationUserType.Admin)] + public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithEditPermission_LimitItemDeletionTrue_ThrowsNotFoundException( + OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, + CurrentContextOrganization organization, SutProvider sutProvider) + { + cipherDetails.UserId = null; + cipherDetails.OrganizationId = organization.Id; + cipherDetails.Type = CipherType.Login; + cipherDetails.Data = JsonSerializer.Serialize(new CipherLoginData()); + cipherDetails.Edit = true; + cipherDetails.Manage = false; // Only Edit permission, not Manage + organization.Type = organizationUserType; + + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); + sutProvider.GetDependency() + .GetManyByUserIdAsync(userId) + .Returns(new List { cipherDetails }); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organization.Id) + .Returns(new OrganizationAbility + { + Id = organization.Id, + LimitItemDeletion = true // Restrictive mode - Edit permission should NOT work + }); + + await Assert.ThrowsAsync(() => sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id)); + } + [Theory] [BitAutoData] public async Task PutRestoreAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException( @@ -1420,10 +1319,14 @@ public class CiphersControllerTests await Assert.ThrowsAsync(() => sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id)); } + + + + [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task PutRestoreManyAdmin_WithOwnerOrAdmin_WithEditPermission_RestoresCiphers( + public async Task PutRestoreManyAdmin_WithOwnerOrAdmin_WithManagePermission_RestoresCiphers( OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId, List ciphers, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -1431,77 +1334,6 @@ public class CiphersControllerTests model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(ciphers.Select(c => new CipherDetails - { - Id = c.Id, - OrganizationId = organization.Id, - Edit = true - }).ToList()); - - var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails - { - Id = c.Id, - OrganizationId = organization.Id - }).ToList(); - - sutProvider.GetDependency() - .RestoreManyAsync(Arg.Is>(ids => - ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()), - userId, organization.Id, true) - .Returns(cipherOrgDetails); - - var result = await sutProvider.Sut.PutRestoreManyAdmin(model); - - await sutProvider.GetDependency().Received(1) - .RestoreManyAsync( - Arg.Is>(ids => - ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()), - userId, organization.Id, true); - } - - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task PutRestoreManyAdmin_WithOwnerOrAdmin_WithoutEditPermission_ThrowsNotFoundException( - OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId, List ciphers, - CurrentContextOrganization organization, SutProvider sutProvider) - { - model.OrganizationId = organization.Id; - model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); - organization.Type = organizationUserType; - - sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - sutProvider.GetDependency() - .GetManyByUserIdAsync(userId) - .Returns(ciphers.Select(c => new CipherDetails - { - Id = c.Id, - OrganizationId = organization.Id, - Edit = false, - Type = CipherType.Login, - Data = JsonSerializer.Serialize(new CipherLoginData()) - }).ToList()); - - await Assert.ThrowsAsync(() => sutProvider.Sut.PutRestoreManyAdmin(model)); - } - - [Theory] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.Admin)] - public async Task PutRestoreManyAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithManagePermission_RestoresCiphers( - OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId, List ciphers, - CurrentContextOrganization organization, SutProvider sutProvider) - { - model.OrganizationId = organization.Id; - model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); - organization.Type = organizationUserType; - - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -1553,7 +1385,7 @@ public class CiphersControllerTests [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] - public async Task PutRestoreManyAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( + public async Task PutRestoreManyAdmin_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId, List ciphers, CurrentContextOrganization organization, SutProvider sutProvider) { @@ -1561,7 +1393,6 @@ public class CiphersControllerTests model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); @@ -1599,6 +1430,7 @@ public class CiphersControllerTests organization.Type = organizationUserType; sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails @@ -1614,9 +1446,16 @@ public class CiphersControllerTests .Returns(cipherOrgDetails); sutProvider.GetDependency() .RestoreManyAsync(Arg.Is>(ids => - ids.All(id => model.Ids.Contains(id.ToString()) && ids.Count == model.Ids.Count())), + ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()), userId, organization.Id, true) .Returns(cipherOrgDetails); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(organization.Id) + .Returns(new OrganizationAbility + { + Id = organization.Id, + LimitItemDeletion = true + }); var result = await sutProvider.Sut.PutRestoreManyAdmin(model); diff --git a/test/Core.Test/Vault/Services/CipherServiceTests.cs b/test/Core.Test/Vault/Services/CipherServiceTests.cs index 95fd8179e3..0941372963 100644 --- a/test/Core.Test/Vault/Services/CipherServiceTests.cs +++ b/test/Core.Test/Vault/Services/CipherServiceTests.cs @@ -673,13 +673,21 @@ public class CipherServiceTests [BitAutoData] public async Task RestoreAsync_UpdatesUserCipher(Guid restoringUserId, CipherDetails cipher, SutProvider sutProvider) { - sutProvider.GetDependency().GetCanEditByIdAsync(restoringUserId, cipher.Id).Returns(true); + cipher.UserId = restoringUserId; + cipher.OrganizationId = null; var initialRevisionDate = new DateTime(1970, 1, 1, 0, 0, 0); cipher.DeletedDate = initialRevisionDate; cipher.RevisionDate = initialRevisionDate; - await sutProvider.Sut.RestoreAsync(cipher, restoringUserId, cipher.OrganizationId.HasValue); + sutProvider.GetDependency() + .GetUserByIdAsync(restoringUserId) + .Returns(new User + { + Id = restoringUserId, + }); + + await sutProvider.Sut.RestoreAsync(cipher, restoringUserId); Assert.Null(cipher.DeletedDate); Assert.NotEqual(initialRevisionDate, cipher.RevisionDate); @@ -688,15 +696,28 @@ public class CipherServiceTests [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task RestoreAsync_UpdatesOrganizationCipher(Guid restoringUserId, CipherDetails cipher, SutProvider sutProvider) + public async Task RestoreAsync_UpdatesOrganizationCipher(Guid restoringUserId, CipherDetails cipher, User user, SutProvider sutProvider) { - sutProvider.GetDependency().GetCanEditByIdAsync(restoringUserId, cipher.Id).Returns(true); + cipher.OrganizationId = Guid.NewGuid(); + cipher.Edit = false; + cipher.Manage = true; + + sutProvider.GetDependency() + .GetUserByIdAsync(restoringUserId) + .Returns(user); + sutProvider.GetDependency() + .GetOrganizationAbilityAsync(cipher.OrganizationId.Value) + .Returns(new OrganizationAbility + { + Id = cipher.OrganizationId.Value, + LimitItemDeletion = true + }); var initialRevisionDate = new DateTime(1970, 1, 1, 0, 0, 0); cipher.DeletedDate = initialRevisionDate; cipher.RevisionDate = initialRevisionDate; - await sutProvider.Sut.RestoreAsync(cipher, restoringUserId, cipher.OrganizationId.HasValue); + await sutProvider.Sut.RestoreAsync(cipher, restoringUserId); Assert.Null(cipher.DeletedDate); Assert.NotEqual(initialRevisionDate, cipher.RevisionDate); @@ -724,24 +745,12 @@ public class CipherServiceTests cipherDetails.UserId = Guid.NewGuid(); cipherDetails.OrganizationId = null; - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.RestoreAsync(cipherDetails, restoringUserId)); - - Assert.Contains("do not have permissions", exception.Message); - await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().UpsertAsync(default); - await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().LogCipherEventAsync(default, default); - await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().PushSyncCipherUpdateAsync(default, default); - } - - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task RestoreAsync_WithOrgCipherLackingEditPermission_ThrowsBadRequestException( - Guid restoringUserId, CipherDetails cipherDetails, SutProvider sutProvider) - { - sutProvider.GetDependency() - .GetCanEditByIdAsync(restoringUserId, cipherDetails.Id) - .Returns(false); + sutProvider.GetDependency() + .GetUserByIdAsync(restoringUserId) + .Returns(new User + { + Id = restoringUserId, + }); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.RestoreAsync(cipherDetails, restoringUserId)); @@ -752,28 +761,6 @@ public class CipherServiceTests await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().PushSyncCipherUpdateAsync(default, default); } - [Theory] - [BitAutoData] - public async Task RestoreAsync_WithEditPermission_RestoresCipherDetails( - Guid restoringUserId, CipherDetails cipherDetails, SutProvider sutProvider) - { - sutProvider.GetDependency() - .GetCanEditByIdAsync(restoringUserId, cipherDetails.Id) - .Returns(true); - - var initialRevisionDate = new DateTime(1970, 1, 1, 0, 0, 0); - cipherDetails.DeletedDate = initialRevisionDate; - cipherDetails.RevisionDate = initialRevisionDate; - - await sutProvider.Sut.RestoreAsync(cipherDetails, restoringUserId); - - Assert.Null(cipherDetails.DeletedDate); - Assert.NotEqual(initialRevisionDate, cipherDetails.RevisionDate); - await sutProvider.GetDependency().Received(1).UpsertAsync(cipherDetails); - await sutProvider.GetDependency().Received(1).LogCipherEventAsync(cipherDetails, EventType.Cipher_Restored); - await sutProvider.GetDependency().Received(1).PushSyncCipherUpdateAsync(cipherDetails, null); - } - [Theory] [OrganizationCipherCustomize] [BitAutoData] @@ -794,7 +781,7 @@ public class CipherServiceTests [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task RestoreAsync_WithLimitItemDeletionEnabled_WithManagePermission_RestoresCipher( + public async Task RestoreAsync_WithManagePermission_RestoresCipher( Guid restoringUserId, CipherDetails cipherDetails, User user, SutProvider sutProvider) { cipherDetails.OrganizationId = Guid.NewGuid(); @@ -802,9 +789,6 @@ public class CipherServiceTests cipherDetails.Edit = false; cipherDetails.Manage = true; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetUserByIdAsync(restoringUserId) .Returns(user); @@ -828,7 +812,7 @@ public class CipherServiceTests [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task RestoreAsync_WithLimitItemDeletionEnabled_WithoutManagePermission_ThrowsBadRequestException( + public async Task RestoreAsync_WithoutManagePermission_ThrowsBadRequestException( Guid restoringUserId, CipherDetails cipherDetails, User user, SutProvider sutProvider) { cipherDetails.OrganizationId = Guid.NewGuid(); @@ -836,9 +820,6 @@ public class CipherServiceTests cipherDetails.Edit = true; cipherDetails.Manage = false; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetUserByIdAsync(restoringUserId) .Returns(user); @@ -859,32 +840,7 @@ public class CipherServiceTests await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().PushSyncCipherUpdateAsync(default, default); } - [Theory] - [BitAutoData] - public async Task RestoreManyAsync_UpdatesCiphers(ICollection ciphers, - SutProvider sutProvider) - { - var cipherIds = ciphers.Select(c => c.Id).ToArray(); - var restoringUserId = ciphers.First().UserId.Value; - var previousRevisionDate = DateTime.UtcNow; - foreach (var cipher in ciphers) - { - cipher.Edit = true; - cipher.RevisionDate = previousRevisionDate; - } - sutProvider.GetDependency().GetManyByUserIdAsync(restoringUserId).Returns(ciphers); - var revisionDate = previousRevisionDate + TimeSpan.FromMinutes(1); - sutProvider.GetDependency().RestoreAsync(Arg.Any>(), restoringUserId).Returns(revisionDate); - - await sutProvider.Sut.RestoreManyAsync(cipherIds, restoringUserId); - - foreach (var cipher in ciphers) - { - Assert.Null(cipher.DeletedDate); - Assert.Equal(revisionDate, cipher.RevisionDate); - } - } [Theory] [BitAutoData] @@ -971,90 +927,14 @@ public class CipherServiceTests .PushSyncCiphersAsync(restoringUserId); } - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task RestoreManyAsync_WithOrgCipherAndEditPermission_RestoresCiphers( - Guid restoringUserId, List ciphers, Guid organizationId, SutProvider sutProvider) - { - var cipherIds = ciphers.Select(c => c.Id).ToArray(); - var previousRevisionDate = DateTime.UtcNow; - foreach (var cipher in ciphers) - { - cipher.OrganizationId = organizationId; - cipher.Edit = true; - cipher.DeletedDate = DateTime.UtcNow; - cipher.RevisionDate = previousRevisionDate; - } - sutProvider.GetDependency() - .GetManyByUserIdAsync(restoringUserId) - .Returns(ciphers); - var revisionDate = previousRevisionDate + TimeSpan.FromMinutes(1); - sutProvider.GetDependency() - .RestoreAsync(Arg.Any>(), restoringUserId) - .Returns(revisionDate); - - var result = await sutProvider.Sut.RestoreManyAsync(cipherIds, restoringUserId); - - Assert.Equal(ciphers.Count, result.Count); - foreach (var cipher in result) - { - Assert.Null(cipher.DeletedDate); - Assert.Equal(revisionDate, cipher.RevisionDate); - } - - await sutProvider.GetDependency() - .Received(1) - .RestoreAsync(Arg.Is>(ids => ids.Count() == cipherIds.Count() && - ids.All(id => cipherIds.Contains(id))), restoringUserId); - await sutProvider.GetDependency() - .Received(1) - .LogCipherEventsAsync(Arg.Any>>()); - await sutProvider.GetDependency() - .Received(1) - .PushSyncCiphersAsync(restoringUserId); - } [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task RestoreManyAsync_WithOrgCipherLackingEditPermission_DoesNotRestoreCiphers( - Guid restoringUserId, List ciphers, Guid organizationId, SutProvider sutProvider) - { - var cipherIds = ciphers.Select(c => c.Id).ToArray(); - var cipherDetailsList = ciphers.Select(c => new CipherDetails - { - Id = c.Id, - OrganizationId = organizationId, - Edit = false, - DeletedDate = DateTime.UtcNow - }).ToList(); - - sutProvider.GetDependency() - .GetManyByUserIdAsync(restoringUserId) - .Returns(cipherDetailsList); - - var result = await sutProvider.Sut.RestoreManyAsync(cipherIds, restoringUserId); - - Assert.Empty(result); - await sutProvider.GetDependency() - .Received(1) - .RestoreAsync(Arg.Is>(ids => !ids.Any()), restoringUserId); - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .LogCipherEventsAsync(Arg.Any>>()); - await sutProvider.GetDependency() - .Received(1) - .PushSyncCiphersAsync(restoringUserId); - } - - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task RestoreManyAsync_WithLimitItemDeletionEnabled_WithManagePermission_RestoresCiphers( + public async Task RestoreManyAsync_WithManagePermission_RestoresCiphers( Guid restoringUserId, List ciphers, User user, SutProvider sutProvider) { var organizationId = Guid.NewGuid(); @@ -1070,9 +950,6 @@ public class CipherServiceTests cipher.RevisionDate = previousRevisionDate; } - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetManyByUserIdAsync(restoringUserId) .Returns(ciphers); @@ -1121,7 +998,7 @@ public class CipherServiceTests [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task RestoreManyAsync_WithLimitItemDeletionEnabled_WithoutManagePermission_DoesNotRestoreCiphers( + public async Task RestoreManyAsync_WithoutManagePermission_DoesNotRestoreCiphers( Guid restoringUserId, List ciphers, User user, SutProvider sutProvider) { var organizationId = Guid.NewGuid(); @@ -1135,9 +1012,6 @@ public class CipherServiceTests cipher.DeletedDate = DateTime.UtcNow; } - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetManyByUserIdAsync(restoringUserId) .Returns(ciphers); @@ -1502,23 +1376,12 @@ public class CipherServiceTests cipherDetails.UserId = deletingUserId; cipherDetails.OrganizationId = null; - await sutProvider.Sut.DeleteAsync(cipherDetails, deletingUserId); - - await sutProvider.GetDependency().Received(1).DeleteAsync(cipherDetails); - await sutProvider.GetDependency().Received(1).DeleteAttachmentsForCipherAsync(cipherDetails.Id); - await sutProvider.GetDependency().Received(1).LogCipherEventAsync(cipherDetails, EventType.Cipher_Deleted); - await sutProvider.GetDependency().Received(1).PushSyncCipherDeleteAsync(cipherDetails); - } - - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task DeleteAsync_WithOrgCipherAndEditPermission_DeletesCipher( - Guid deletingUserId, CipherDetails cipherDetails, SutProvider sutProvider) - { - sutProvider.GetDependency() - .GetCanEditByIdAsync(deletingUserId, cipherDetails.Id) - .Returns(true); + sutProvider.GetDependency() + .GetUserByIdAsync(deletingUserId) + .Returns(new User + { + Id = deletingUserId, + }); await sutProvider.Sut.DeleteAsync(cipherDetails, deletingUserId); @@ -1528,6 +1391,8 @@ public class CipherServiceTests await sutProvider.GetDependency().Received(1).PushSyncCipherDeleteAsync(cipherDetails); } + + [Theory] [BitAutoData] public async Task DeleteAsync_WithPersonalCipherBelongingToDifferentUser_ThrowsBadRequestException( @@ -1536,25 +1401,12 @@ public class CipherServiceTests cipherDetails.UserId = Guid.NewGuid(); cipherDetails.OrganizationId = null; - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.DeleteAsync(cipherDetails, deletingUserId)); - - Assert.Contains("do not have permissions", exception.Message); - await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().DeleteAsync(default); - await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().DeleteAttachmentsForCipherAsync(default); - await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().LogCipherEventAsync(default, default); - await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().PushSyncCipherDeleteAsync(default); - } - - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task DeleteAsync_WithOrgCipherLackingEditPermission_ThrowsBadRequestException( - Guid deletingUserId, CipherDetails cipherDetails, SutProvider sutProvider) - { - sutProvider.GetDependency() - .GetCanEditByIdAsync(deletingUserId, cipherDetails.Id) - .Returns(false); + sutProvider.GetDependency() + .GetUserByIdAsync(deletingUserId) + .Returns(new User + { + Id = deletingUserId, + }); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.DeleteAsync(cipherDetails, deletingUserId)); @@ -1583,16 +1435,13 @@ public class CipherServiceTests [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task DeleteAsync_WithLimitItemDeletionEnabled_WithManagePermission_DeletesCipher( + public async Task DeleteAsync_WithManagePermission_DeletesCipher( Guid deletingUserId, CipherDetails cipherDetails, User user, SutProvider sutProvider) { cipherDetails.OrganizationId = Guid.NewGuid(); cipherDetails.Edit = false; cipherDetails.Manage = true; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetUserByIdAsync(deletingUserId) .Returns(user); @@ -1615,16 +1464,13 @@ public class CipherServiceTests [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task DeleteAsync_WithLimitItemDeletionEnabled_WithoutManagePermission_ThrowsBadRequestException( + public async Task DeleteAsync_WithoutManagePermission_ThrowsBadRequestException( Guid deletingUserId, CipherDetails cipherDetails, User user, SutProvider sutProvider) { cipherDetails.OrganizationId = Guid.NewGuid(); cipherDetails.Edit = true; cipherDetails.Manage = false; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetUserByIdAsync(deletingUserId) .Returns(user); @@ -1691,6 +1537,12 @@ public class CipherServiceTests cipher.Edit = true; } + sutProvider.GetDependency() + .GetUserByIdAsync(deletingUserId) + .Returns(new User + { + Id = deletingUserId, + }); sutProvider.GetDependency() .GetManyByUserIdAsync(deletingUserId) .Returns(ciphers); @@ -1740,71 +1592,14 @@ public class CipherServiceTests .PushSyncCiphersAsync(deletingUserId); } - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task DeleteManyAsync_WithOrgCipherAndEditPermission_DeletesCiphers( - Guid deletingUserId, List ciphers, Guid organizationId, SutProvider sutProvider) - { - var cipherIds = ciphers.Select(c => c.Id).ToArray(); - foreach (var cipher in ciphers) - { - cipher.OrganizationId = organizationId; - cipher.Edit = true; - } - sutProvider.GetDependency() - .GetManyByUserIdAsync(deletingUserId) - .Returns(ciphers); - await sutProvider.Sut.DeleteManyAsync(cipherIds, deletingUserId, organizationId); - await sutProvider.GetDependency() - .Received(1) - .DeleteAsync(Arg.Is>(ids => ids.Count() == cipherIds.Count() && ids.All(id => cipherIds.Contains(id))), deletingUserId); - await sutProvider.GetDependency() - .Received(1) - .LogCipherEventsAsync(Arg.Any>>()); - await sutProvider.GetDependency() - .Received(1) - .PushSyncCiphersAsync(deletingUserId); - } [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task DeleteManyAsync_WithOrgCipherLackingEditPermission_DoesNotDeleteCiphers( - Guid deletingUserId, List ciphers, Guid organizationId, SutProvider sutProvider) - { - var cipherIds = ciphers.Select(c => c.Id).ToArray(); - var cipherDetailsList = ciphers.Select(c => new CipherDetails - { - Id = c.Id, - OrganizationId = organizationId, - Edit = false - }).ToList(); - - sutProvider.GetDependency() - .GetManyByUserIdAsync(deletingUserId) - .Returns(cipherDetailsList); - - await sutProvider.Sut.DeleteManyAsync(cipherIds, deletingUserId, organizationId); - - await sutProvider.GetDependency() - .Received(1) - .DeleteAsync(Arg.Is>(ids => !ids.Any()), deletingUserId); - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .LogCipherEventsAsync(Arg.Any>>()); - await sutProvider.GetDependency() - .Received(1) - .PushSyncCiphersAsync(deletingUserId); - } - - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task DeleteManyAsync_WithLimitItemDeletionEnabled_WithoutManagePermission_DoesNotDeleteCiphers( + public async Task DeleteManyAsync_WithoutManagePermission_DoesNotDeleteCiphers( Guid deletingUserId, List ciphers, User user, SutProvider sutProvider) { var organizationId = Guid.NewGuid(); @@ -1817,9 +1612,6 @@ public class CipherServiceTests cipher.Manage = false; } - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetManyByUserIdAsync(deletingUserId) .Returns(ciphers); @@ -1855,7 +1647,7 @@ public class CipherServiceTests [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task DeleteManyAsync_WithLimitItemDeletionEnabled_WithManagePermission_DeletesCiphers( + public async Task DeleteManyAsync_WithManagePermission_DeletesCiphers( Guid deletingUserId, List ciphers, User user, SutProvider sutProvider) { var organizationId = Guid.NewGuid(); @@ -1868,9 +1660,6 @@ public class CipherServiceTests cipher.Manage = true; } - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetManyByUserIdAsync(deletingUserId) .Returns(ciphers); @@ -1913,9 +1702,12 @@ public class CipherServiceTests cipherDetails.OrganizationId = null; cipherDetails.DeletedDate = null; - sutProvider.GetDependency() - .GetCanEditByIdAsync(deletingUserId, cipherDetails.Id) - .Returns(true); + sutProvider.GetDependency() + .GetUserByIdAsync(deletingUserId) + .Returns(new User + { + Id = deletingUserId, + }); await sutProvider.Sut.SoftDeleteAsync(cipherDetails, deletingUserId); @@ -1926,26 +1718,7 @@ public class CipherServiceTests await sutProvider.GetDependency().Received(1).PushSyncCipherUpdateAsync(cipherDetails, null); } - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task SoftDeleteAsync_WithOrgCipherAndEditPermission_SoftDeletesCipher( - Guid deletingUserId, CipherDetails cipherDetails, SutProvider sutProvider) - { - cipherDetails.DeletedDate = null; - sutProvider.GetDependency() - .GetCanEditByIdAsync(deletingUserId, cipherDetails.Id) - .Returns(true); - - await sutProvider.Sut.SoftDeleteAsync(cipherDetails, deletingUserId); - - Assert.NotNull(cipherDetails.DeletedDate); - Assert.Equal(cipherDetails.RevisionDate, cipherDetails.DeletedDate); - await sutProvider.GetDependency().Received(1).UpsertAsync(cipherDetails); - await sutProvider.GetDependency().Received(1).LogCipherEventAsync(cipherDetails, EventType.Cipher_SoftDeleted); - await sutProvider.GetDependency().Received(1).PushSyncCipherUpdateAsync(cipherDetails, null); - } [Theory] [BitAutoData] @@ -1955,9 +1728,12 @@ public class CipherServiceTests cipherDetails.UserId = Guid.NewGuid(); cipherDetails.OrganizationId = null; - sutProvider.GetDependency() - .GetCanEditByIdAsync(deletingUserId, cipherDetails.Id) - .Returns(false); + sutProvider.GetDependency() + .GetUserByIdAsync(deletingUserId) + .Returns(new User + { + Id = deletingUserId, + }); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.SoftDeleteAsync(cipherDetails, deletingUserId)); @@ -1965,48 +1741,23 @@ public class CipherServiceTests Assert.Contains("do not have permissions", exception.Message); } - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task SoftDeleteAsync_WithOrgCipherLackingEditPermission_ThrowsBadRequestException( - Guid deletingUserId, CipherDetails cipherDetails, SutProvider sutProvider) - { - sutProvider.GetDependency() - .GetCanEditByIdAsync(deletingUserId, cipherDetails.Id) - .Returns(false); - - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.SoftDeleteAsync(cipherDetails, deletingUserId)); - - Assert.Contains("do not have permissions", exception.Message); - } - - [Theory] - [BitAutoData] - public async Task SoftDeleteAsync_WithEditPermission_SoftDeletesCipherDetails( - Guid deletingUserId, CipherDetails cipherDetails, SutProvider sutProvider) - { - cipherDetails.DeletedDate = null; - - await sutProvider.Sut.SoftDeleteAsync(cipherDetails, deletingUserId, true); - - Assert.NotNull(cipherDetails.DeletedDate); - Assert.Equal(cipherDetails.RevisionDate, cipherDetails.DeletedDate); - await sutProvider.GetDependency().Received(1).UpsertAsync(cipherDetails); - await sutProvider.GetDependency().Received(1).LogCipherEventAsync(cipherDetails, EventType.Cipher_SoftDeleted); - await sutProvider.GetDependency().Received(1).PushSyncCipherUpdateAsync(cipherDetails, null); - } - [Theory] [BitAutoData] public async Task SoftDeleteAsync_WithAlreadySoftDeletedCipher_SkipsOperation( Guid deletingUserId, CipherDetails cipherDetails, SutProvider sutProvider) { - sutProvider.GetDependency() - .GetCanEditByIdAsync(deletingUserId, cipherDetails.Id) - .Returns(true); + // Set up as personal cipher owned by the deleting user + cipherDetails.UserId = deletingUserId; + cipherDetails.OrganizationId = null; cipherDetails.DeletedDate = DateTime.UtcNow.AddDays(-1); + sutProvider.GetDependency() + .GetUserByIdAsync(deletingUserId) + .Returns(new User + { + Id = deletingUserId, + }); + await sutProvider.Sut.SoftDeleteAsync(cipherDetails, deletingUserId); await sutProvider.GetDependency().DidNotReceive().UpsertAsync(Arg.Any()); @@ -2032,7 +1783,7 @@ public class CipherServiceTests [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task SoftDeleteAsync_WithLimitItemDeletionEnabled_WithManagePermission_SoftDeletesCipher( + public async Task SoftDeleteAsync_WithManagePermission_SoftDeletesCipher( Guid deletingUserId, CipherDetails cipherDetails, User user, SutProvider sutProvider) { cipherDetails.OrganizationId = Guid.NewGuid(); @@ -2040,9 +1791,6 @@ public class CipherServiceTests cipherDetails.Edit = false; cipherDetails.Manage = true; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetUserByIdAsync(deletingUserId) .Returns(user); @@ -2066,7 +1814,7 @@ public class CipherServiceTests [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task SoftDeleteAsync_WithLimitItemDeletionEnabled_WithoutManagePermission_ThrowsBadRequestException( + public async Task SoftDeleteAsync_WithoutManagePermission_ThrowsBadRequestException( Guid deletingUserId, CipherDetails cipherDetails, User user, SutProvider sutProvider) { cipherDetails.OrganizationId = Guid.NewGuid(); @@ -2074,9 +1822,6 @@ public class CipherServiceTests cipherDetails.Edit = true; cipherDetails.Manage = false; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetUserByIdAsync(deletingUserId) .Returns(user); @@ -2143,6 +1888,12 @@ public class CipherServiceTests cipher.DeletedDate = null; } + sutProvider.GetDependency() + .GetUserByIdAsync(deletingUserId) + .Returns(new User + { + Id = deletingUserId, + }); sutProvider.GetDependency() .GetManyByUserIdAsync(deletingUserId) .Returns(ciphers); @@ -2192,72 +1943,14 @@ public class CipherServiceTests .PushSyncCiphersAsync(deletingUserId); } - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task SoftDeleteManyAsync_WithOrgCipherAndEditPermission_SoftDeletesCiphers( - Guid deletingUserId, List ciphers, Guid organizationId, SutProvider sutProvider) - { - var cipherIds = ciphers.Select(c => c.Id).ToArray(); - foreach (var cipher in ciphers) - { - cipher.OrganizationId = organizationId; - cipher.Edit = true; - cipher.DeletedDate = null; - } - sutProvider.GetDependency() - .GetManyByUserIdAsync(deletingUserId) - .Returns(ciphers); - await sutProvider.Sut.SoftDeleteManyAsync(cipherIds, deletingUserId, organizationId, false); - await sutProvider.GetDependency() - .Received(1) - .SoftDeleteAsync(Arg.Is>(ids => ids.Count() == cipherIds.Count() && ids.All(id => cipherIds.Contains(id))), deletingUserId); - await sutProvider.GetDependency() - .Received(1) - .LogCipherEventsAsync(Arg.Any>>()); - await sutProvider.GetDependency() - .Received(1) - .PushSyncCiphersAsync(deletingUserId); - } [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task SoftDeleteManyAsync_WithOrgCipherLackingEditPermission_DoesNotDeleteCiphers( - Guid deletingUserId, List ciphers, Guid organizationId, SutProvider sutProvider) - { - var cipherIds = ciphers.Select(c => c.Id).ToArray(); - var cipherDetailsList = ciphers.Select(c => new CipherDetails - { - Id = c.Id, - OrganizationId = organizationId, - Edit = false - }).ToList(); - - sutProvider.GetDependency() - .GetManyByUserIdAsync(deletingUserId) - .Returns(cipherDetailsList); - - await sutProvider.Sut.SoftDeleteManyAsync(cipherIds, deletingUserId, organizationId, false); - - await sutProvider.GetDependency() - .Received(1) - .SoftDeleteAsync(Arg.Is>(ids => !ids.Any()), deletingUserId); - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .LogCipherEventsAsync(Arg.Any>>()); - await sutProvider.GetDependency() - .Received(1) - .PushSyncCiphersAsync(deletingUserId); - } - - [Theory] - [OrganizationCipherCustomize] - [BitAutoData] - public async Task SoftDeleteManyAsync_WithLimitItemDeletionEnabled_WithoutManagePermission_DoesNotDeleteCiphers( + public async Task SoftDeleteManyAsync_WithoutManagePermission_DoesNotDeleteCiphers( Guid deletingUserId, List ciphers, User user, SutProvider sutProvider) { var organizationId = Guid.NewGuid(); @@ -2270,9 +1963,6 @@ public class CipherServiceTests cipher.Manage = false; } - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetManyByUserIdAsync(deletingUserId) .Returns(ciphers); @@ -2308,7 +1998,7 @@ public class CipherServiceTests [Theory] [OrganizationCipherCustomize] [BitAutoData] - public async Task SoftDeleteManyAsync_WithLimitItemDeletionEnabled_WithManagePermission_SoftDeletesCiphers( + public async Task SoftDeleteManyAsync_WithManagePermission_SoftDeletesCiphers( Guid deletingUserId, List ciphers, User user, SutProvider sutProvider) { var organizationId = Guid.NewGuid(); @@ -2322,9 +2012,6 @@ public class CipherServiceTests cipher.DeletedDate = null; } - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.LimitItemDeletion) - .Returns(true); sutProvider.GetDependency() .GetManyByUserIdAsync(deletingUserId) .Returns(ciphers);