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);