using Bit.Api.Controllers; using Bit.Api.Models.Request; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.Models.Data; using Bit.Core.OrganizationFeatures.OrganizationCollections.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using Xunit; namespace Bit.Api.Test.Controllers; [ControllerCustomize(typeof(CollectionsController))] [SutProviderCustomize] public class CollectionsControllerTests { [Theory, BitAutoData] public async Task Post_Success(Guid orgId, SutProvider sutProvider) { sutProvider.GetDependency() .CreateNewCollections(orgId) .Returns(true); sutProvider.GetDependency() .EditAnyCollection(orgId) .Returns(false); var collectionRequest = new CollectionRequestModel { Name = "encrypted_string", ExternalId = "my_external_id" }; _ = await sutProvider.Sut.Post(orgId, collectionRequest); await sutProvider.GetDependency() .Received(1) .SaveAsync(Arg.Any(), Arg.Any>(), null); } [Theory, BitAutoData] public async Task Put_Success(Guid orgId, Guid collectionId, Guid userId, CollectionRequestModel collectionRequest, SutProvider sutProvider) { sutProvider.GetDependency() .ViewAssignedCollections(orgId) .Returns(true); sutProvider.GetDependency() .EditAssignedCollections(orgId) .Returns(true); sutProvider.GetDependency() .UserId .Returns(userId); sutProvider.GetDependency() .GetByIdAsync(collectionId, userId) .Returns(new CollectionDetails { OrganizationId = orgId, }); _ = await sutProvider.Sut.Put(orgId, collectionId, collectionRequest); } [Theory, BitAutoData] public async Task Put_CanNotEditAssignedCollection_ThrowsNotFound(Guid orgId, Guid collectionId, Guid userId, CollectionRequestModel collectionRequest, SutProvider sutProvider) { sutProvider.GetDependency() .EditAssignedCollections(orgId) .Returns(true); sutProvider.GetDependency() .UserId .Returns(userId); sutProvider.GetDependency() .GetByIdAsync(collectionId, userId) .Returns(Task.FromResult(null)); _ = await Assert.ThrowsAsync(async () => await sutProvider.Sut.Put(orgId, collectionId, collectionRequest)); } [Theory, BitAutoData] public async Task GetOrganizationCollectionsWithGroups_NoManagerPermissions_ThrowsNotFound(Organization organization, SutProvider sutProvider) { sutProvider.GetDependency().ViewAssignedCollections(organization.Id).Returns(false); await Assert.ThrowsAsync(() => sutProvider.Sut.GetManyWithDetails(organization.Id)); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().GetManyByOrganizationIdWithAccessAsync(default); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().GetManyByUserIdWithAccessAsync(default, default); } [Theory, BitAutoData] public async Task GetOrganizationCollectionsWithGroups_AdminPermissions_GetsAllCollections(Organization organization, User user, SutProvider sutProvider) { sutProvider.GetDependency().UserId.Returns(user.Id); sutProvider.GetDependency().ViewAllCollections(organization.Id).Returns(true); sutProvider.GetDependency().OrganizationAdmin(organization.Id).Returns(true); await sutProvider.Sut.GetManyWithDetails(organization.Id); await sutProvider.GetDependency().Received().GetManyByOrganizationIdWithAccessAsync(organization.Id); await sutProvider.GetDependency().Received().GetManyByUserIdWithAccessAsync(user.Id, organization.Id); } [Theory, BitAutoData] public async Task GetOrganizationCollectionsWithGroups_MissingViewAllPermissions_GetsAssignedCollections(Organization organization, User user, SutProvider sutProvider) { sutProvider.GetDependency().UserId.Returns(user.Id); sutProvider.GetDependency().ViewAssignedCollections(organization.Id).Returns(true); sutProvider.GetDependency().OrganizationManager(organization.Id).Returns(true); await sutProvider.Sut.GetManyWithDetails(organization.Id); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().GetManyByOrganizationIdWithAccessAsync(default); await sutProvider.GetDependency().Received().GetManyByUserIdWithAccessAsync(user.Id, organization.Id); } [Theory, BitAutoData] public async Task GetOrganizationCollectionsWithGroups_CustomUserWithManagerPermissions_GetsAssignedCollections(Organization organization, User user, SutProvider sutProvider) { sutProvider.GetDependency().UserId.Returns(user.Id); sutProvider.GetDependency().ViewAssignedCollections(organization.Id).Returns(true); sutProvider.GetDependency().EditAssignedCollections(organization.Id).Returns(true); await sutProvider.Sut.GetManyWithDetails(organization.Id); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().GetManyByOrganizationIdWithAccessAsync(default); await sutProvider.GetDependency().Received().GetManyByUserIdWithAccessAsync(user.Id, organization.Id); } [Theory, BitAutoData] public async Task DeleteMany_Success(Guid orgId, User user, Collection collection1, Collection collection2, SutProvider sutProvider) { // Arrange var model = new CollectionBulkDeleteRequestModel { Ids = new[] { collection1.Id.ToString(), collection2.Id.ToString() }, OrganizationId = orgId.ToString() }; var collections = new List { new CollectionDetails { Id = collection1.Id, OrganizationId = orgId, }, new CollectionDetails { Id = collection2.Id, OrganizationId = orgId, }, }; sutProvider.GetDependency() .DeleteAssignedCollections(orgId) .Returns(true); sutProvider.GetDependency() .UserId .Returns(user.Id); sutProvider.GetDependency() .GetManyByUserIdAsync(user.Id) .Returns(collections); // Act await sutProvider.Sut.DeleteMany(model); // Assert await sutProvider.GetDependency() .Received(1) .DeleteManyAsync(Arg.Is>(coll => coll.Select(c => c.Id).SequenceEqual(collections.Select(c => c.Id)))); } [Theory, BitAutoData] public async Task DeleteMany_CanNotDeleteAssignedCollection_ThrowsNotFound(Guid orgId, Collection collection1, Collection collection2, SutProvider sutProvider) { // Arrange var model = new CollectionBulkDeleteRequestModel { Ids = new[] { collection1.Id.ToString(), collection2.Id.ToString() }, OrganizationId = orgId.ToString() }; sutProvider.GetDependency() .DeleteAssignedCollections(orgId) .Returns(false); // Assert await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteMany(model)); await sutProvider.GetDependency() .DidNotReceiveWithAnyArgs() .DeleteManyAsync((IEnumerable)default); } [Theory, BitAutoData] public async Task DeleteMany_UserCanNotAccessCollections_FiltersOutInvalid(Guid orgId, User user, Collection collection1, Collection collection2, SutProvider sutProvider) { // Arrange var model = new CollectionBulkDeleteRequestModel { Ids = new[] { collection1.Id.ToString(), collection2.Id.ToString() }, OrganizationId = orgId.ToString() }; var collections = new List { new CollectionDetails { Id = collection2.Id, OrganizationId = orgId, }, }; sutProvider.GetDependency() .DeleteAssignedCollections(orgId) .Returns(true); sutProvider.GetDependency() .UserId .Returns(user.Id); sutProvider.GetDependency() .GetManyByUserIdAsync(user.Id) .Returns(collections); // Act await sutProvider.Sut.DeleteMany(model); // Assert await sutProvider.GetDependency() .Received(1) .DeleteManyAsync(Arg.Is>(coll => coll.Select(c => c.Id).SequenceEqual(collections.Select(c => c.Id)))); } }