using System.Security.Claims; using System.Text.Json; using Bit.Api.Vault.Controllers; 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; using Bit.Core.Exceptions; using Bit.Core.Models.Data.Organizations; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Vault.Entities; using Bit.Core.Vault.Models.Data; using Bit.Core.Vault.Repositories; using Bit.Core.Vault.Services; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using NSubstitute.ReturnsExtensions; using Xunit; using CipherType = Bit.Core.Vault.Enums.CipherType; namespace Bit.Api.Test.Controllers; [ControllerCustomize(typeof(CiphersController))] [SutProviderCustomize] public class CiphersControllerTests { [Theory, BitAutoData] public async Task PutPartialShouldReturnCipherWithGivenFolderAndFavoriteValues(User user, Guid folderId, SutProvider<CiphersController> sutProvider) { var isFavorite = true; var cipherId = Guid.NewGuid(); sutProvider.GetDependency<IUserService>() .GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()) .Returns(user); var cipherDetails = new CipherDetails { UserId = user.Id, Favorite = isFavorite, FolderId = folderId, Type = Core.Vault.Enums.CipherType.SecureNote, Data = "{}" }; sutProvider.GetDependency<ICipherRepository>() .GetByIdAsync(cipherId, user.Id) .Returns(Task.FromResult(cipherDetails)); var result = await sutProvider.Sut.PutPartial(cipherId, new CipherPartialRequestModel { Favorite = isFavorite, FolderId = folderId.ToString() }); Assert.Equal(folderId, result.FolderId); Assert.Equal(isFavorite, result.Favorite); } [Theory, BitAutoData] public async Task PutCollections_vNextShouldThrowExceptionWhenCipherIsNullOrNoOrgValue(Guid id, CipherCollectionsRequestModel model, User user, SutProvider<CiphersController> sutProvider) { sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); sutProvider.GetDependency<ICurrentContext>().OrganizationUser(Guid.NewGuid()).Returns(false); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(id, user.Id).ReturnsNull(); var requestAction = async () => await sutProvider.Sut.PutCollections_vNext(id, model); await Assert.ThrowsAsync<NotFoundException>(requestAction); } [Theory, BitAutoData] public async Task PutCollections_vNextShouldSaveUpdatedCipher(Guid id, CipherCollectionsRequestModel model, Guid userId, SutProvider<CiphersController> sutProvider) { SetupUserAndOrgMocks(id, userId, sutProvider); var cipherDetails = CreateCipherDetailsMock(id, userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(id, userId).ReturnsForAnyArgs(cipherDetails); sutProvider.GetDependency<ICollectionCipherRepository>().GetManyByUserIdCipherIdAsync(userId, id).Returns((ICollection<CollectionCipher>)new List<CollectionCipher>()); sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(new Dictionary<Guid, OrganizationAbility> { { cipherDetails.OrganizationId.Value, new OrganizationAbility() } }); var cipherService = sutProvider.GetDependency<ICipherService>(); await sutProvider.Sut.PutCollections_vNext(id, model); await cipherService.ReceivedWithAnyArgs().SaveCollectionsAsync(default, default, default, default); } [Theory, BitAutoData] public async Task PutCollections_vNextReturnOptionalDetailsCipherUnavailableFalse(Guid id, CipherCollectionsRequestModel model, Guid userId, SutProvider<CiphersController> sutProvider) { SetupUserAndOrgMocks(id, userId, sutProvider); var cipherDetails = CreateCipherDetailsMock(id, userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(id, userId).ReturnsForAnyArgs(cipherDetails); sutProvider.GetDependency<ICollectionCipherRepository>().GetManyByUserIdCipherIdAsync(userId, id).Returns((ICollection<CollectionCipher>)new List<CollectionCipher>()); sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(new Dictionary<Guid, OrganizationAbility> { { cipherDetails.OrganizationId.Value, new OrganizationAbility() } }); var result = await sutProvider.Sut.PutCollections_vNext(id, model); Assert.IsType<OptionalCipherDetailsResponseModel>(result); Assert.False(result.Unavailable); } [Theory, BitAutoData] public async Task PutCollections_vNextReturnOptionalDetailsCipherUnavailableTrue(Guid id, CipherCollectionsRequestModel model, Guid userId, SutProvider<CiphersController> sutProvider) { SetupUserAndOrgMocks(id, userId, sutProvider); var cipherDetails = CreateCipherDetailsMock(id, userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(id, userId).ReturnsForAnyArgs(cipherDetails, [(CipherDetails)null]); sutProvider.GetDependency<ICollectionCipherRepository>().GetManyByUserIdCipherIdAsync(userId, id).Returns((ICollection<CollectionCipher>)new List<CollectionCipher>()); var result = await sutProvider.Sut.PutCollections_vNext(id, model); Assert.IsType<OptionalCipherDetailsResponseModel>(result); Assert.True(result.Unavailable); } private void SetupUserAndOrgMocks(Guid id, Guid userId, SutProvider<CiphersController> sutProvider) { sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().OrganizationUser(default).ReturnsForAnyArgs(true); sutProvider.GetDependency<ICollectionCipherRepository>().GetManyByUserIdCipherIdAsync(userId, id).Returns(new List<CollectionCipher>()); } private CipherDetails CreateCipherDetailsMock(Guid id, Guid userId) { return new CipherDetails { Id = id, UserId = userId, OrganizationId = Guid.NewGuid(), Type = CipherType.Login, ViewPassword = true, Data = @" { ""Uris"": [ { ""Uri"": ""https://bitwarden.com"" } ], ""Username"": ""testuser"", ""Password"": ""securepassword123"" }" }; } [Theory] [BitAutoData(OrganizationUserType.Admin, true, true)] [BitAutoData(OrganizationUserType.Owner, true, true)] [BitAutoData(OrganizationUserType.Custom, false, true)] [BitAutoData(OrganizationUserType.Custom, true, true)] [BitAutoData(OrganizationUserType.Admin, false, false)] [BitAutoData(OrganizationUserType.Owner, false, false)] [BitAutoData(OrganizationUserType.Custom, false, false)] public async Task CanEditCiphersAsAdminAsync_FlexibleCollections_Success( OrganizationUserType userType, bool allowAdminsAccessToAllItems, bool shouldSucceed, CurrentContextOrganization organization, Guid userId, CipherDetails cipherDetails, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; organization.Type = userType; if (userType == OrganizationUserType.Custom) { // Assume custom users have EditAnyCollections for success case organization.Permissions.EditAnyCollection = shouldSucceed; } sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility { Id = organization.Id, AllowAdminAccessToAllCollectionItems = allowAdminsAccessToAllItems }); if (shouldSucceed) { await sutProvider.Sut.DeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().ReceivedWithAnyArgs() .DeleteAsync(default, default); } else { await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipherDetails.Id)); await sutProvider.GetDependency<ICipherService>().DidNotReceiveWithAnyArgs() .DeleteAsync(default, default); } } [Theory] [BitAutoData(false)] [BitAutoData(false)] [BitAutoData(true)] public async Task CanEditCiphersAsAdminAsync_Providers( bool restrictProviders, CipherDetails cipherDetails, CurrentContextOrganization organization, Guid userId, SutProvider<CiphersController> sutProvider ) { cipherDetails.OrganizationId = organization.Id; // Simulate that the user is a provider for the organization sutProvider.GetDependency<ICurrentContext>().EditAnyCollection(organization.Id).Returns(true); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility { Id = organization.Id, AllowAdminAccessToAllCollectionItems = false }); sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(restrictProviders); // Non restricted providers should succeed if (!restrictProviders) { await sutProvider.Sut.DeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().ReceivedWithAnyArgs() .DeleteAsync(default, default); } else // Otherwise, they should fail { await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipherDetails.Id)); await sutProvider.GetDependency<ICipherService>().DidNotReceiveWithAnyArgs() .DeleteAsync(default, default); } await sutProvider.GetDependency<ICurrentContext>().Received().ProviderUserForOrgAsync(organization.Id); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task DeleteAdmin_WithOwnerOrAdmin_WithEditPermission_DeletesCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Edit = true; cipherDetails.Manage = false; organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); await sutProvider.Sut.DeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().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<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Edit = false; cipherDetails.Manage = false; organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipherDetails.Id)); await sutProvider.GetDependency<ICipherService>().DidNotReceive().DeleteAsync(Arg.Any<CipherDetails>(), Arg.Any<Guid>(), Arg.Any<bool>()); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task DeleteAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithManagePermission_DeletesCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Edit = true; cipherDetails.Manage = true; organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); await sutProvider.Sut.DeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipherDetails, userId, true); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task DeleteAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Edit = true; cipherDetails.Manage = false; organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipherDetails.Id)); await sutProvider.GetDependency<ICipherService>().DidNotReceive().DeleteAsync(Arg.Any<CipherDetails>(), Arg.Any<Guid>(), Arg.Any<bool>()); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task DeleteAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCipher_DeletesCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) .Returns(new List<CipherOrganizationDetails> { new() { Id = cipherDetails.Id } }); await sutProvider.Sut.DeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipherDetails, userId, true); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task DeleteAdmin_WithAdminOrOwner_WithAccessToAllCollectionItems_DeletesCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility { Id = organization.Id, AllowAdminAccessToAllCollectionItems = true }); await sutProvider.Sut.DeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipherDetails, userId, true); } [Theory] [BitAutoData] public async Task DeleteAdmin_WithCustomUser_WithEditAnyCollectionTrue_DeletesCipher( CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = true; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails }); await sutProvider.Sut.DeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipherDetails, userId, true); } [Theory] [BitAutoData] public async Task DeleteAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException( Cipher cipher, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipher.OrganizationId = organization.Id; organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = false; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipher.Id)); } [Theory] [BitAutoData] public async Task DeleteAdmin_WithProviderUser_DeletesCipher( CipherDetails cipherDetails, Guid userId, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = Guid.NewGuid(); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipherDetails.OrganizationId.Value).Returns(true); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(cipherDetails.OrganizationId.Value).Returns(new List<Cipher> { cipherDetails }); await sutProvider.Sut.DeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipherDetails, userId, true); } [Theory] [BitAutoData] public async Task DeleteAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException( Cipher cipher, Guid userId, SutProvider<CiphersController> sutProvider) { cipher.OrganizationId = Guid.NewGuid(); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipher.OrganizationId.Value).Returns(true); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher); sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipher.Id)); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task DeleteManyAdmin_WithOwnerOrAdmin_WithEditPermission_DeletesCiphers( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .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<ICipherService>() .Received(1) .DeleteManyAsync( Arg.Is<IEnumerable<Guid>>(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<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>() .GetProperUserId(default) .ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>() .GetOrganization(new Guid(model.OrganizationId)) .Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByOrganizationIdAsync(new Guid(model.OrganizationId)) .Returns(ciphers); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(new Guid(model.OrganizationId)) .Returns(new OrganizationAbility { Id = new Guid(model.OrganizationId), AllowAdminAccessToAllCollectionItems = false, }); await Assert.ThrowsAsync<NotFoundException>(() => 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<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(ciphers.Select(c => new CipherDetails { Id = c.Id, OrganizationId = organization.Id, Edit = true, Manage = true }).ToList()); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); await sutProvider.Sut.DeleteManyAdmin(model); await sutProvider.GetDependency<ICipherService>() .Received(1) .DeleteManyAsync( Arg.Is<IEnumerable<Guid>>(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_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(ciphers.Select(c => new CipherDetails { Id = c.Id, OrganizationId = organization.Id, Edit = true, Manage = false }).ToList()); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteManyAdmin(model)); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task DeleteManyAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCiphers_DeletesCiphers( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) .Returns(ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id }).ToList()); await sutProvider.Sut.DeleteManyAdmin(model); await sutProvider.GetDependency<ICipherService>() .Received(1) .DeleteManyAsync( Arg.Is<IEnumerable<Guid>>(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_WithAccessToAllCollectionItems_DeletesCiphers( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers); sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility { Id = organization.Id, AllowAdminAccessToAllCollectionItems = true }); await sutProvider.Sut.DeleteManyAdmin(model); await sutProvider.GetDependency<ICipherService>() .Received(1) .DeleteManyAsync( Arg.Is<IEnumerable<Guid>>(ids => ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()), userId, organization.Id, true); } [Theory] [BitAutoData] public async Task DeleteManyAdmin_WithCustomUser_WithEditAnyCollectionTrue_DeletesCiphers( CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = true; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers); await sutProvider.Sut.DeleteManyAdmin(model); await sutProvider.GetDependency<ICipherService>() .Received(1) .DeleteManyAsync( Arg.Is<IEnumerable<Guid>>(ids => ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()), userId, organization.Id, true); } [Theory] [BitAutoData] public async Task DeleteManyAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException( CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = false; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteManyAdmin(model)); } [Theory] [BitAutoData] public async Task DeleteManyAdmin_WithProviderUser_DeletesCiphers( CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, SutProvider<CiphersController> sutProvider) { var organizationId = Guid.NewGuid(); model.OrganizationId = organizationId.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); foreach (var cipher in ciphers) { cipher.OrganizationId = organizationId; } sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organizationId).Returns(true); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organizationId).Returns(ciphers); await sutProvider.Sut.DeleteManyAdmin(model); await sutProvider.GetDependency<ICipherService>() .Received(1) .DeleteManyAsync( Arg.Is<IEnumerable<Guid>>(ids => ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()), userId, organizationId, true); } [Theory] [BitAutoData] public async Task DeleteManyAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException( CipherBulkDeleteRequestModel model, SutProvider<CiphersController> sutProvider) { var organizationId = Guid.NewGuid(); model.OrganizationId = organizationId.ToString(); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organizationId).Returns(true); sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true); await Assert.ThrowsAsync<NotFoundException>(() => 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<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Edit = true; organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); await sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().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<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Edit = false; cipherDetails.Manage = false; organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id)); await sutProvider.GetDependency<ICipherService>().DidNotReceive().SoftDeleteAsync(Arg.Any<CipherDetails>(), Arg.Any<Guid>(), Arg.Any<bool>()); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutDeleteAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithManagePermission_SoftDeletesCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Edit = true; cipherDetails.Manage = true; organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); await sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().Received(1).SoftDeleteAsync(cipherDetails, userId, true); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutDeleteAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Edit = true; cipherDetails.Manage = false; organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id)); await sutProvider.GetDependency<ICipherService>() .DidNotReceiveWithAnyArgs() .SoftDeleteManyAsync(default, default, default, default); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCipher_SoftDeletesCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) .Returns(new List<CipherOrganizationDetails> { new() { Id = cipherDetails.Id } }); await sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().Received(1).SoftDeleteAsync(cipherDetails, userId, true); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithAccessToAllCollectionItems_SoftDeletesCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility { Id = organization.Id, AllowAdminAccessToAllCollectionItems = true }); await sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().Received(1).SoftDeleteAsync(cipherDetails, userId, true); } [Theory] [BitAutoData] public async Task PutDeleteAdmin_WithCustomUser_WithEditAnyCollectionTrue_SoftDeletesCipher( CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = true; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails }); await sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().Received(1).SoftDeleteAsync(cipherDetails, userId, true); } [Theory] [BitAutoData] public async Task PutDeleteAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException( Cipher cipher, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipher.OrganizationId = organization.Id; organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = false; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutDeleteAdmin(cipher.Id)); } [Theory] [BitAutoData] public async Task PutDeleteAdmin_WithProviderUser_SoftDeletesCipher( CipherDetails cipherDetails, Guid userId, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = Guid.NewGuid(); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipherDetails.OrganizationId.Value).Returns(true); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(cipherDetails.OrganizationId.Value).Returns(new List<Cipher> { cipherDetails }); await sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id); await sutProvider.GetDependency<ICipherService>().Received(1).SoftDeleteAsync(cipherDetails, userId, true); } [Theory] [BitAutoData] public async Task PutDeleteAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException( Cipher cipher, Guid userId, SutProvider<CiphersController> sutProvider) { cipher.OrganizationId = Guid.NewGuid(); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipher.OrganizationId.Value).Returns(true); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher); sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutDeleteAdmin(cipher.Id)); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutDeleteManyAdmin_WithOwnerOrAdmin_WithEditPermission_SoftDeletesCiphers( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .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<ICipherService>() .Received(1) .SoftDeleteManyAsync( Arg.Is<IEnumerable<Guid>>(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<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(ciphers.Select(c => new CipherDetails { Id = c.Id, OrganizationId = organization.Id, Edit = false }).ToList()); await Assert.ThrowsAsync<NotFoundException>(() => 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<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(ciphers.Select(c => new CipherDetails { Id = c.Id, OrganizationId = organization.Id, Edit = true, Manage = true }).ToList()); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); await sutProvider.Sut.PutDeleteManyAdmin(model); await sutProvider.GetDependency<ICipherService>() .Received(1) .SoftDeleteManyAsync( Arg.Is<IEnumerable<Guid>>(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_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(ciphers.Select(c => new CipherDetails { Id = c.Id, OrganizationId = organization.Id, Edit = true, Manage = false }).ToList()); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutDeleteManyAdmin(model)); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutDeleteManyAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCiphers_SoftDeletesCiphers( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) .Returns(ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id }).ToList()); await sutProvider.Sut.PutDeleteManyAdmin(model); await sutProvider.GetDependency<ICipherService>() .Received(1) .SoftDeleteManyAsync( Arg.Is<IEnumerable<Guid>>(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_WithAccessToAllCollectionItems_SoftDeletesCiphers( OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers); sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility { Id = organization.Id, AllowAdminAccessToAllCollectionItems = true }); await sutProvider.Sut.PutDeleteManyAdmin(model); await sutProvider.GetDependency<ICipherService>() .Received(1) .SoftDeleteManyAsync( Arg.Is<IEnumerable<Guid>>(ids => ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()), userId, organization.Id, true); } [Theory] [BitAutoData] public async Task PutDeleteManyAdmin_WithCustomUser_WithEditAnyCollectionTrue_SoftDeletesCiphers( CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = true; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers); await sutProvider.Sut.PutDeleteManyAdmin(model); await sutProvider.GetDependency<ICipherService>() .Received(1) .SoftDeleteManyAsync( Arg.Is<IEnumerable<Guid>>(ids => ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()), userId, organization.Id, true); } [Theory] [BitAutoData] public async Task PutDeleteManyAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException( CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = false; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutDeleteManyAdmin(model)); } [Theory] [BitAutoData] public async Task PutDeleteManyAdmin_WithProviderUser_SoftDeletesCiphers( CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers, SutProvider<CiphersController> sutProvider) { var organizationId = Guid.NewGuid(); model.OrganizationId = organizationId.ToString(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); foreach (var cipher in ciphers) { cipher.OrganizationId = organizationId; } sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organizationId).Returns(true); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organizationId).Returns(ciphers); await sutProvider.Sut.PutDeleteManyAdmin(model); await sutProvider.GetDependency<ICipherService>() .Received(1) .SoftDeleteManyAsync( Arg.Is<IEnumerable<Guid>>(ids => ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()), userId, organizationId, true); } [Theory] [BitAutoData] public async Task PutDeleteManyAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException( CipherBulkDeleteRequestModel model, SutProvider<CiphersController> sutProvider) { var organizationId = Guid.NewGuid(); model.OrganizationId = organizationId.ToString(); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organizationId).Returns(true); sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true); await Assert.ThrowsAsync<NotFoundException>(() => 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<CiphersController> 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<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); var result = await sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id); Assert.IsType<CipherMiniResponseModel>(result); await sutProvider.GetDependency<ICipherService>().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<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Edit = false; cipherDetails.Manage = false; organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id)); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutRestoreAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithManagePermission_RestoresCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Type = CipherType.Login; cipherDetails.Data = JsonSerializer.Serialize(new CipherLoginData()); cipherDetails.Edit = true; cipherDetails.Manage = true; organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); var result = await sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id); Assert.IsType<CipherMiniResponseModel>(result); await sutProvider.GetDependency<ICipherService>().Received(1).RestoreAsync(cipherDetails, userId, true); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutRestoreAdmin_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.UserId = null; cipherDetails.OrganizationId = organization.Id; cipherDetails.Edit = true; cipherDetails.Manage = false; organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(new List<CipherDetails> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id)); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCipher_RestoresCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; cipherDetails.Type = CipherType.Login; cipherDetails.Data = JsonSerializer.Serialize(new CipherLoginData()); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) .Returns(new List<CipherOrganizationDetails> { new() { Id = cipherDetails.Id } }); var result = await sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id); Assert.IsType<CipherMiniResponseModel>(result); await sutProvider.GetDependency<ICipherService>().Received(1).RestoreAsync(cipherDetails, userId, true); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithAccessToAllCollectionItems_RestoresCipher( OrganizationUserType organizationUserType, CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; cipherDetails.Type = CipherType.Login; cipherDetails.Data = JsonSerializer.Serialize(new CipherLoginData()); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails }); sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility { Id = organization.Id, AllowAdminAccessToAllCollectionItems = true }); var result = await sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id); Assert.IsType<CipherMiniResponseModel>(result); await sutProvider.GetDependency<ICipherService>().Received(1).RestoreAsync(cipherDetails, userId, true); } [Theory] [BitAutoData] public async Task PutRestoreAdmin_WithCustomUser_WithEditAnyCollectionTrue_RestoresCipher( CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; cipherDetails.Type = CipherType.Login; cipherDetails.Data = JsonSerializer.Serialize(new CipherLoginData()); organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = true; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails }); var result = await sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id); Assert.IsType<CipherMiniResponseModel>(result); await sutProvider.GetDependency<ICipherService>().Received(1).RestoreAsync(cipherDetails, userId, true); } [Theory] [BitAutoData] public async Task PutRestoreAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException( CipherDetails cipherDetails, Guid userId, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = organization.Id; cipherDetails.Type = CipherType.Login; cipherDetails.Data = JsonSerializer.Serialize(new CipherLoginData()); organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = false; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipherDetails.Id).Returns(cipherDetails); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipherDetails }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id)); } [Theory] [BitAutoData] public async Task PutRestoreAdmin_WithProviderUser_RestoresCipher( CipherDetails cipherDetails, Guid userId, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = Guid.NewGuid(); cipherDetails.Type = CipherType.Login; cipherDetails.Data = JsonSerializer.Serialize(new CipherLoginData()); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipherDetails.OrganizationId.Value).Returns(true); sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(cipherDetails.OrganizationId.Value).Returns(new List<Cipher> { cipherDetails }); var result = await sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id); Assert.IsType<CipherMiniResponseModel>(result); await sutProvider.GetDependency<ICipherService>().Received(1).RestoreAsync(cipherDetails, userId, true); } [Theory] [BitAutoData] public async Task PutRestoreAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException( CipherDetails cipherDetails, Guid userId, SutProvider<CiphersController> sutProvider) { cipherDetails.OrganizationId = Guid.NewGuid(); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipherDetails.OrganizationId.Value).Returns(true); sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipherDetails.Id).Returns(cipherDetails); sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id)); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutRestoreManyAdmin_WithOwnerOrAdmin_WithEditPermission_RestoresCiphers( OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id; model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .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<ICipherService>() .RestoreManyAsync(Arg.Is<HashSet<Guid>>(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<ICipherService>().Received(1) .RestoreManyAsync( Arg.Is<HashSet<Guid>>(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<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id; model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .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<NotFoundException>(() => 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<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id; model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(ciphers.Select(c => new CipherDetails { Id = c.Id, OrganizationId = organization.Id, Edit = true, Manage = true }).ToList()); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id, OrganizationId = organization.Id, Type = CipherType.Login, Data = JsonSerializer.Serialize(new CipherLoginData()) }).ToList(); sutProvider.GetDependency<ICipherService>() .RestoreManyAsync( Arg.Is<HashSet<Guid>>(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); Assert.Equal(ciphers.Count, result.Data.Count()); await sutProvider.GetDependency<ICipherService>() .Received(1) .RestoreManyAsync( Arg.Is<HashSet<Guid>>(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_WithLimitItemDeletionEnabled_WithOwnerOrAdmin_WithoutManagePermission_ThrowsNotFoundException( OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id; model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitItemDeletion).Returns(true); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId }); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>() .GetManyByUserIdAsync(userId) .Returns(ciphers.Select(c => new CipherDetails { Id = c.Id, OrganizationId = organization.Id, Edit = true, Manage = false, Type = CipherType.Login, Data = JsonSerializer.Serialize(new CipherLoginData()) }).ToList()); sutProvider.GetDependency<IApplicationCacheService>() .GetOrganizationAbilityAsync(organization.Id) .Returns(new OrganizationAbility { Id = organization.Id, LimitItemDeletion = true }); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreManyAdmin(model)); } [Theory] [BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Admin)] public async Task PutRestoreManyAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCiphers_RestoresCiphers( OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id; model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id, OrganizationId = organization.Id, Type = CipherType.Login, Data = JsonSerializer.Serialize(new CipherLoginData()) }).ToList(); sutProvider.GetDependency<ICipherRepository>() .GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id) .Returns(cipherOrgDetails); sutProvider.GetDependency<ICipherService>() .RestoreManyAsync(Arg.Is<HashSet<Guid>>(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); Assert.NotNull(result); Assert.Equal(model.Ids.Count(), result.Data.Count()); await sutProvider.GetDependency<ICipherService>() .Received(1) .RestoreManyAsync( Arg.Is<HashSet<Guid>>(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_WithAccessToAllCollectionItems_RestoresCiphers( OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id; model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = organizationUserType; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers); sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility { Id = organization.Id, AllowAdminAccessToAllCollectionItems = true }); var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id, OrganizationId = organization.Id, Type = CipherType.Login, Data = JsonSerializer.Serialize(new CipherLoginData()) }).ToList(); sutProvider.GetDependency<ICipherService>() .RestoreManyAsync(Arg.Any<HashSet<Guid>>(), userId, organization.Id, true) .Returns(cipherOrgDetails); var result = await sutProvider.Sut.PutRestoreManyAdmin(model); Assert.NotNull(result); Assert.Equal(ciphers.Count, result.Data.Count()); await sutProvider.GetDependency<ICipherService>().Received(1) .RestoreManyAsync( Arg.Is<HashSet<Guid>>(ids => ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()), userId, organization.Id, true); } [Theory] [BitAutoData] public async Task PutRestoreManyAdmin_WithCustomUser_WithEditAnyCollectionTrue_RestoresCiphers( CipherBulkRestoreRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id; model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = true; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers); var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id, OrganizationId = organization.Id, Type = CipherType.Login, Data = JsonSerializer.Serialize(new CipherLoginData()) }).ToList(); sutProvider.GetDependency<ICipherService>() .RestoreManyAsync( Arg.Is<HashSet<Guid>>(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); Assert.NotNull(result); Assert.Equal(ciphers.Count, result.Data.Count()); await sutProvider.GetDependency<ICipherService>() .Received(1) .RestoreManyAsync( Arg.Is<HashSet<Guid>>(ids => ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()), userId, organization.Id, true); } [Theory] [BitAutoData] public async Task PutRestoreManyAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException( CipherBulkRestoreRequestModel model, Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider) { model.OrganizationId = organization.Id; model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); organization.Type = OrganizationUserType.Custom; organization.Permissions.EditAnyCollection = false; sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreManyAdmin(model)); } [Theory] [BitAutoData] public async Task PutRestoreManyAdmin_WithProviderUser_RestoresCiphers( CipherBulkRestoreRequestModel model, Guid userId, List<Cipher> ciphers, SutProvider<CiphersController> sutProvider) { model.OrganizationId = Guid.NewGuid(); model.Ids = ciphers.Select(c => c.Id.ToString()).ToList(); sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(model.OrganizationId).Returns(true); sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(model.OrganizationId).Returns(ciphers); var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id, OrganizationId = model.OrganizationId }).ToList(); sutProvider.GetDependency<ICipherService>() .RestoreManyAsync( Arg.Any<HashSet<Guid>>(), userId, model.OrganizationId, true) .Returns(cipherOrgDetails); var result = await sutProvider.Sut.PutRestoreManyAdmin(model); Assert.NotNull(result); await sutProvider.GetDependency<ICipherService>() .Received(1) .RestoreManyAsync( Arg.Is<HashSet<Guid>>(ids => ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()), userId, model.OrganizationId, true); } [Theory] [BitAutoData] public async Task PutRestoreManyAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException( CipherBulkRestoreRequestModel model, SutProvider<CiphersController> sutProvider) { model.OrganizationId = Guid.NewGuid(); sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(model.OrganizationId).Returns(true); sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreManyAdmin(model)); } }