using System.Security.Claims; using AutoFixture; using Bit.Api.Models.Request; using Bit.Api.Tools.Controllers; using Bit.Api.Tools.Models.Request.Accounts; using Bit.Api.Tools.Models.Request.Organizations; using Bit.Api.Vault.AuthorizationHandlers.Collections; using Bit.Api.Vault.Models.Request; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.Repositories; using Bit.Core.Tools.ImportFeatures.Interfaces; using Bit.Core.Vault.Entities; using Bit.Core.Vault.Models.Data; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using Microsoft.AspNetCore.Authorization; using NSubstitute; using Xunit; using GlobalSettings = Bit.Core.Settings.GlobalSettings; namespace Bit.Api.Test.Tools.Controllers; [ControllerCustomize(typeof(ImportCiphersController))] [SutProviderCustomize] public class ImportCiphersControllerTests { /************************* * PostImport - Individual *************************/ [Theory, BitAutoData] public async Task PostImportIndividual_ImportCiphersRequestModel_BadRequestException(SutProvider sutProvider, IFixture fixture) { // Arrange sutProvider.GetDependency() .SelfHosted = false; var ciphers = fixture.CreateMany(7001).ToArray(); var model = new ImportCiphersRequestModel { Ciphers = ciphers, FolderRelationships = null, Folders = null }; // Act var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.PostImport(model)); // Assert Assert.Equal("You cannot import this much data at once.", exception.Message); } [Theory, BitAutoData] public async Task PostImportIndividual_ImportCiphersRequestModel_Success(User user, IFixture fixture, SutProvider sutProvider) { // Arrange sutProvider.GetDependency() .SelfHosted = false; sutProvider.GetDependency() .GetProperUserId(Arg.Any()) .Returns(user.Id); var request = fixture.Build() .With(x => x.Ciphers, fixture.Build() .With(c => c.OrganizationId, Guid.NewGuid().ToString()) .With(c => c.FolderId, Guid.NewGuid().ToString()) .CreateMany(1).ToArray()) .Create(); // Act await sutProvider.Sut.PostImport(request); // Assert await sutProvider.GetDependency() .Received() .ImportIntoIndividualVaultAsync( Arg.Any>(), Arg.Any>(), Arg.Any>>() ); } /**************************** * PostImport - Organization ****************************/ [Theory, BitAutoData] public async Task PostImportOrganization_ImportOrganizationCiphersRequestModel_BadRequestException(SutProvider sutProvider, IFixture fixture) { // Arrange var globalSettings = sutProvider.GetDependency(); globalSettings.SelfHosted = false; globalSettings.ImportCiphersLimitation = new GlobalSettings.ImportCiphersLimitationSettings() { // limits are set in appsettings.json, making values small for test to run faster. CiphersLimit = 200, CollectionsLimit = 400, CollectionRelationshipsLimit = 20 }; var ciphers = fixture.CreateMany(201).ToArray(); var model = new ImportOrganizationCiphersRequestModel { Collections = null, Ciphers = ciphers, CollectionRelationships = null }; // Act var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.PostImport(Arg.Any(), model)); // Assert Assert.Equal("You cannot import this much data at once.", exception.Message); } [Theory, BitAutoData] public async Task PostImportOrganization_ImportOrganizationCiphersRequestModel_Succeeds( SutProvider sutProvider, IFixture fixture, User user) { // Arrange var orgId = "AD89E6F8-4E84-4CFE-A978-256CC0DBF974"; var orgIdGuid = Guid.Parse(orgId); var existingCollections = fixture.CreateMany(2).ToArray(); sutProvider.GetDependency().SelfHosted = false; sutProvider.GetDependency() .GetProperUserId(Arg.Any()) .Returns(user.Id); var request = fixture.Build() .With(x => x.Ciphers, fixture.Build() .With(c => c.OrganizationId, Guid.NewGuid().ToString()) .With(c => c.FolderId, Guid.NewGuid().ToString()) .CreateMany(1).ToArray()) .With(y => y.Collections, fixture.Build() .With(c => c.Id, orgIdGuid) .CreateMany(1).ToArray()) .Create(); // AccessImportExport permission setup sutProvider.GetDependency() .AccessImportExport(Arg.Any()) .Returns(false); // BulkCollectionOperations.ImportCiphers permission setup sutProvider.GetDependency() .AuthorizeAsync(Arg.Any(), Arg.Any>(), Arg.Is>(reqs => reqs.Contains(BulkCollectionOperations.ImportCiphers))) .Returns(AuthorizationResult.Success()); // BulkCollectionOperations.Create permission setup sutProvider.GetDependency() .AuthorizeAsync(Arg.Any(), Arg.Any>(), Arg.Is>(reqs => reqs.Contains(BulkCollectionOperations.Create))) .Returns(AuthorizationResult.Success()); sutProvider.GetDependency() .GetManyByOrganizationIdAsync(orgIdGuid) .Returns(existingCollections.Select(c => new Collection { Id = orgIdGuid }).ToList()); // Act await sutProvider.Sut.PostImport(orgId, request); // Assert await sutProvider.GetDependency() .Received(1) .ImportIntoOrganizationalVaultAsync( Arg.Any>(), Arg.Any>(), Arg.Any>>(), Arg.Any()); } [Theory, BitAutoData] public async Task PostImportOrganization_WithAccessImportExport_Succeeds( SutProvider sutProvider, IFixture fixture, User user) { // Arrange var orgId = "AD89E6F8-4E84-4CFE-A978-256CC0DBF974"; var orgIdGuid = Guid.Parse(orgId); var existingCollections = fixture.CreateMany(2).ToArray(); sutProvider.GetDependency().SelfHosted = false; sutProvider.GetDependency() .GetProperUserId(Arg.Any()) .Returns(user.Id); var request = fixture.Build() .With(x => x.Ciphers, fixture.Build() .With(c => c.OrganizationId, Guid.NewGuid().ToString()) .With(c => c.FolderId, Guid.NewGuid().ToString()) .CreateMany(1).ToArray()) .With(y => y.Collections, fixture.Build() .With(c => c.Id, orgIdGuid) .CreateMany(1).ToArray()) .Create(); // AccessImportExport permission setup sutProvider.GetDependency() .AccessImportExport(Arg.Any()) .Returns(false); // BulkCollectionOperations.ImportCiphers permission setup sutProvider.GetDependency() .AuthorizeAsync(Arg.Any(), Arg.Any>(), Arg.Is>(reqs => reqs.Contains(BulkCollectionOperations.ImportCiphers))) .Returns(AuthorizationResult.Success()); // BulkCollectionOperations.Create permission setup sutProvider.GetDependency() .AuthorizeAsync(Arg.Any(), Arg.Any>(), Arg.Is>(reqs => reqs.Contains(BulkCollectionOperations.Create))) .Returns(AuthorizationResult.Success()); sutProvider.GetDependency() .GetManyByOrganizationIdAsync(orgIdGuid) .Returns(existingCollections.Select(c => new Collection { Id = orgIdGuid }).ToList()); // Act await sutProvider.Sut.PostImport(orgId, request); // Assert await sutProvider.GetDependency() .Received(1) .ImportIntoOrganizationalVaultAsync( Arg.Any>(), Arg.Any>(), Arg.Any>>(), Arg.Any()); } [Theory, BitAutoData] public async Task PostImportOrganization_WithExistingCollectionsAndWithoutImportCiphersPermissions_NotFoundException( SutProvider sutProvider, IFixture fixture, User user) { // Arrange var orgId = "AD89E6F8-4E84-4CFE-A978-256CC0DBF974"; var orgIdGuid = Guid.Parse(orgId); var existingCollections = fixture.CreateMany(2).ToArray(); sutProvider.GetDependency().SelfHosted = false; sutProvider.GetDependency() .GetProperUserId(Arg.Any()) .Returns(user.Id); var request = fixture.Build() .With(x => x.Ciphers, fixture.Build() .With(c => c.OrganizationId, Guid.NewGuid().ToString()) .With(c => c.FolderId, Guid.NewGuid().ToString()) .CreateMany(1).ToArray()) .With(y => y.Collections, fixture.Build() .With(c => c.Id, orgIdGuid) .CreateMany(1).ToArray()) .Create(); // AccessImportExport permission setup sutProvider.GetDependency() .AccessImportExport(Arg.Any()) .Returns(false); // BulkCollectionOperations.ImportCiphers permission setup sutProvider.GetDependency() .AuthorizeAsync(Arg.Any(), Arg.Any>(), Arg.Is>(reqs => reqs.Contains(BulkCollectionOperations.ImportCiphers))) .Returns(AuthorizationResult.Failed()); // BulkCollectionOperations.Create permission setup sutProvider.GetDependency() .AuthorizeAsync(Arg.Any(), Arg.Any>(), Arg.Is>(reqs => reqs.Contains(BulkCollectionOperations.Create))) .Returns(AuthorizationResult.Success()); sutProvider.GetDependency() .GetManyByOrganizationIdAsync(orgIdGuid) .Returns(existingCollections.Select(c => new Collection { Id = orgIdGuid }).ToList()); // Act var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.PostImport(orgId, request)); // Assert Assert.IsType(exception); } [Theory, BitAutoData] public async Task PostImportOrganization_WithoutCreatePermissions_NotFoundException( SutProvider sutProvider, IFixture fixture, User user) { // Arrange var orgId = "AD89E6F8-4E84-4CFE-A978-256CC0DBF974"; var orgIdGuid = Guid.Parse(orgId); var existingCollections = fixture.CreateMany(2).ToArray(); sutProvider.GetDependency().SelfHosted = false; sutProvider.GetDependency() .GetProperUserId(Arg.Any()) .Returns(user.Id); var request = fixture.Build() .With(x => x.Ciphers, fixture.Build() .With(c => c.OrganizationId, Guid.NewGuid().ToString()) .With(c => c.FolderId, Guid.NewGuid().ToString()) .CreateMany(1).ToArray()) .With(y => y.Collections, fixture.Build() .With(c => c.Id, orgIdGuid) .CreateMany(1).ToArray()) .Create(); // AccessImportExport permission setup sutProvider.GetDependency() .AccessImportExport(Arg.Any()) .Returns(false); // BulkCollectionOperations.ImportCiphers permission setup sutProvider.GetDependency() .AuthorizeAsync(Arg.Any(), Arg.Any>(), Arg.Is>(reqs => reqs.Contains(BulkCollectionOperations.ImportCiphers))) .Returns(AuthorizationResult.Success()); // BulkCollectionOperations.Create permission setup sutProvider.GetDependency() .AuthorizeAsync(Arg.Any(), Arg.Any>(), Arg.Is>(reqs => reqs.Contains(BulkCollectionOperations.Create))) .Returns(AuthorizationResult.Failed()); sutProvider.GetDependency() .GetManyByOrganizationIdAsync(orgIdGuid) .Returns(existingCollections.Select(c => new Collection { Id = orgIdGuid }).ToList()); // Act var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.PostImport(orgId, request)); // Assert Assert.IsType(exception); } }