using Bit.Api.Controllers; using Bit.Api.Models.Request; using Bit.Core.AdminConsole.Entities; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; 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; using Collection = Bit.Core.Entities.Collection; using User = Bit.Core.Entities.User; namespace Bit.Api.Test.Controllers; /// /// CollectionsController tests that use pre-Flexible Collections logic. To be removed when the feature flag is removed. /// Note the feature flag defaults to OFF so it is not explicitly set in these tests. /// [ControllerCustomize(typeof(CollectionsController))] [SutProviderCustomize] public class LegacyCollectionsControllerTests { [Theory, BitAutoData] public async Task Post_Manager_AssignsToCollection_Success(Guid orgId, OrganizationUser orgUser, SutProvider sutProvider) { orgUser.Type = OrganizationUserType.Manager; orgUser.Status = OrganizationUserStatusType.Confirmed; sutProvider.GetDependency() .OrganizationManager(orgId) .Returns(true); sutProvider.GetDependency() .EditAnyCollection(orgId) .Returns(false); sutProvider.GetDependency() .EditAssignedCollections(orgId) .Returns(true); sutProvider.GetDependency().UserId = orgUser.UserId; sutProvider.GetDependency().GetByOrganizationAsync(orgId, orgUser.UserId.Value) .Returns(orgUser); var collectionRequest = new CollectionRequestModel { Name = "encrypted_string", ExternalId = "my_external_id" }; _ = await sutProvider.Sut.Post(orgId, collectionRequest); var test = sutProvider.GetDependency().ReceivedCalls(); await sutProvider.GetDependency() .Received(1) .SaveAsync(Arg.Any(), Arg.Any>(), Arg.Is>(users => users.Any(u => u.Id == orgUser.Id && !u.ReadOnly && !u.HidePasswords && !u.Manage))); } [Theory, BitAutoData] public async Task Post_Owner_DoesNotAssignToCollection_Success(Guid orgId, OrganizationUser orgUser, SutProvider sutProvider) { orgUser.Type = OrganizationUserType.Owner; orgUser.Status = OrganizationUserStatusType.Confirmed; sutProvider.GetDependency() .OrganizationManager(orgId) .Returns(true); sutProvider.GetDependency() .EditAnyCollection(orgId) .Returns(true); sutProvider.GetDependency() .EditAssignedCollections(orgId) .Returns(true); sutProvider.GetDependency().UserId = orgUser.UserId; sutProvider.GetDependency().GetByOrganizationAsync(orgId, orgUser.UserId.Value) .Returns(orgUser); var collectionRequest = new CollectionRequestModel { Name = "encrypted_string", ExternalId = "my_external_id" }; _ = await sutProvider.Sut.Post(orgId, collectionRequest); var test = sutProvider.GetDependency().ReceivedCalls(); await sutProvider.GetDependency() .Received(1) .SaveAsync(Arg.Any(), Arg.Any>(), Arg.Is>(users => !users.Any())); } [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, Arg.Any()) .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, Arg.Any()) .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, 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, Arg.Any()); } [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, Arg.Any()); } [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, Arg.Any()); } [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, collection2.Id }, }; 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() .GetOrganizationCollectionsAsync(orgId) .Returns(collections); // Act await sutProvider.Sut.DeleteMany(orgId, 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, collection2.Id }, }; sutProvider.GetDependency() .DeleteAssignedCollections(orgId) .Returns(false); // Assert await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteMany(orgId, 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, collection2.Id }, }; 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() .GetOrganizationCollectionsAsync(orgId) .Returns(collections); // Act await sutProvider.Sut.DeleteMany(orgId, model); // Assert await sutProvider.GetDependency() .Received(1) .DeleteManyAsync(Arg.Is>(coll => coll.Select(c => c.Id).SequenceEqual(collections.Select(c => c.Id)))); } }