diff --git a/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs b/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs index 2136a64d7d..774dfb4fba 100644 --- a/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs +++ b/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs @@ -42,21 +42,13 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler targetCollections, CurrentContextOrganization org) { if (org.Type is OrganizationUserType.Owner or OrganizationUserType.Admin || + org.Permissions.EditAnyCollection || org.Permissions.DeleteAnyCollection || await _currentContext.ProviderUserForOrgAsync(org.Id)) { context.Succeed(requirement); diff --git a/src/Api/Vault/AuthorizationHandlers/Collections/CollectionAuthorizationHandler.cs b/src/Api/Vault/AuthorizationHandlers/Collections/CollectionAuthorizationHandler.cs index f01726a878..041d6e3d44 100644 --- a/src/Api/Vault/AuthorizationHandlers/Collections/CollectionAuthorizationHandler.cs +++ b/src/Api/Vault/AuthorizationHandlers/Collections/CollectionAuthorizationHandler.cs @@ -61,8 +61,6 @@ public class CollectionAuthorizationHandler : AuthorizationHandler sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = userType; + organization.Permissions.CreateNewCollections = createNewCollection; + organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Create }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData, CollectionCustomization] + public async Task CanCreateAsync_WhenProviderUser_Success( + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.User; + organization.Permissions.CreateNewCollections = false; + organization.LimitCollectionCreationDeletion = true; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Create }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().ProviderUserForOrgAsync(organization.Id).Returns(true); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, CollectionCustomization] + [BitAutoData(OrganizationUserType.User)] + [BitAutoData(OrganizationUserType.Custom)] + public async Task CanCreateAsync_WhenMissingCreateCollectionPermission_Failure( + OrganizationUserType userType, + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = userType; + organization.Permissions.CreateNewCollections = false; + organization.LimitCollectionCreationDeletion = true; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Create }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.False(context.HasSucceeded); + } + + [Theory, CollectionCustomization] + [BitAutoData(OrganizationUserType.Admin)] + [BitAutoData(OrganizationUserType.Owner)] + public async Task CanReadAsync_WhenAdminOrOwner_Success( + OrganizationUserType userType, + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = userType; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Read }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData, CollectionCustomization] + public async Task CanReadAsync_WhenProviderUser_Success( + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.User; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Read }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().ProviderUserForOrgAsync(organization.Id).Returns(true); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, CollectionCustomization] + [BitAutoData(true, false)] + [BitAutoData(false, true)] + public async Task CanReadAsync_WhenCustomUserWithRequiredPermissions_Success( + bool editAnyCollection, bool deleteAnyCollection, + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.Custom; + organization.Permissions.EditAnyCollection = editAnyCollection; + organization.Permissions.DeleteAnyCollection = deleteAnyCollection; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Read }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData, CollectionCustomization] + public async Task CanReadAsync_WhenUserAssigned_Success( + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.User; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Read }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetManyByUserIdAsync(actingUserId).Returns(collections); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, CollectionCustomization] + [BitAutoData(OrganizationUserType.User)] + [BitAutoData(OrganizationUserType.Custom)] + public async Task CanReadAsync_WhenMissingAccess_Failure( + OrganizationUserType userType, + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = userType; + organization.Permissions.EditAnyCollection = false; + organization.Permissions.DeleteAnyCollection = false; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Read }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.False(context.HasSucceeded); + } + + [Theory, CollectionCustomization] + [BitAutoData(OrganizationUserType.Admin)] + [BitAutoData(OrganizationUserType.Owner)] + public async Task CanReadAccessAsync_WhenAdminOrOwner_Success( + OrganizationUserType userType, + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = userType; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAccess }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData, CollectionCustomization] + public async Task CanReadAccessAsync_WhenProviderUser_Success( + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.User; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAccess }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().ProviderUserForOrgAsync(organization.Id).Returns(true); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, CollectionCustomization] + [BitAutoData(true, false)] + [BitAutoData(false, true)] + public async Task CanReadAccessAsync_WhenCustomUserWithRequiredPermissions_Success( + bool editAnyCollection, bool deleteAnyCollection, + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.Custom; + organization.Permissions.EditAnyCollection = editAnyCollection; + organization.Permissions.DeleteAnyCollection = deleteAnyCollection; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAccess }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData, CollectionCustomization] + public async Task CanReadAccessAsync_WhenUserAssigned_Success( + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.User; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAccess }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetManyByUserIdAsync(actingUserId).Returns(collections); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, CollectionCustomization] + [BitAutoData(OrganizationUserType.User)] + [BitAutoData(OrganizationUserType.Custom)] + public async Task CanReadAccessAsync_WhenMissingAccess_Failure( + OrganizationUserType userType, + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = userType; + organization.Permissions.EditAnyCollection = false; + organization.Permissions.DeleteAnyCollection = false; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAccess }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.False(context.HasSucceeded); + } + + [Theory, CollectionCustomization] + [BitAutoData(OrganizationUserType.User, false, true)] + [BitAutoData(OrganizationUserType.Admin, false, false)] + [BitAutoData(OrganizationUserType.Owner, false, false)] + [BitAutoData(OrganizationUserType.Custom, true, false)] + [BitAutoData(OrganizationUserType.Owner, true, true)] + public async Task CanUpdateAsync_Success( + OrganizationUserType userType, bool editAnyCollection, bool manageCollections, + SutProvider sutProvider, + ICollection collections, + ICollection collectionDetails, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + foreach (var collectionDetail in collectionDetails) + { + collectionDetail.Manage = manageCollections; + } + + organization.Type = userType; + organization.Permissions.EditAnyCollection = editAnyCollection; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Update }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData, CollectionCustomization] + public async Task CanUpdateAsync_WhenProviderUser_Success( + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.User; + organization.Permissions.EditAnyCollection = false; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Update }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().ProviderUserForOrgAsync(organization.Id).Returns(true); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData, CollectionCustomization] + public async Task CanUpdateAsync_WhenMissingManageCollectionPermission_Failure( + SutProvider sutProvider, + ICollection collections, + ICollection collectionDetails, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + foreach (var collectionDetail in collectionDetails) + { + collectionDetail.Manage = true; + } + // Simulate one collection missing the manage permission + collectionDetails.First().Manage = false; + + // Ensure the user is not an owner/admin and does not have edit any collection permission + organization.Type = OrganizationUserType.User; + organization.Permissions.EditAnyCollection = false; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Update }, + new ClaimsPrincipal(), + collections + ); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(Arg.Any()).Returns(organization); + sutProvider.GetDependency().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails); + + await sutProvider.Sut.HandleAsync(context); + Assert.False(context.HasSucceeded); + sutProvider.GetDependency().ReceivedWithAnyArgs().GetOrganization(default); + await sutProvider.GetDependency().ReceivedWithAnyArgs() + .GetManyByUserIdAsync(default); + } + [Theory, CollectionCustomization] [BitAutoData(OrganizationUserType.User, false, true)] [BitAutoData(OrganizationUserType.Admin, false, false)] @@ -57,36 +499,68 @@ public class BulkCollectionAuthorizationHandlerTests Assert.True(context.HasSucceeded); } - [Theory, CollectionCustomization] - [BitAutoData(OrganizationUserType.User, false, false)] - [BitAutoData(OrganizationUserType.Admin, false, true)] - [BitAutoData(OrganizationUserType.Owner, false, true)] - [BitAutoData(OrganizationUserType.Custom, true, true)] - public async Task CanCreateAsync_Success( - OrganizationUserType userType, bool createNewCollection, bool limitCollectionCreateDelete, + [Theory, BitAutoData, CollectionCustomization] + public async Task CanManageCollectionAccessAsync_WhenProviderUser_Success( SutProvider sutProvider, ICollection collections, CurrentContextOrganization organization) { var actingUserId = Guid.NewGuid(); - organization.Type = userType; - organization.Permissions.CreateNewCollections = createNewCollection; - organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete; + organization.Type = OrganizationUserType.User; + organization.Permissions.EditAnyCollection = false; var context = new AuthorizationHandlerContext( - new[] { CollectionOperations.Create }, + new[] { CollectionOperations.ModifyAccess }, new ClaimsPrincipal(), collections); sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().ProviderUserForOrgAsync(organization.Id).Returns(true); await sutProvider.Sut.HandleAsync(context); Assert.True(context.HasSucceeded); } + [Theory, BitAutoData, CollectionCustomization] + public async Task CanManageCollectionAccessAsync_WhenMissingManageCollectionPermission_Failure( + SutProvider sutProvider, + ICollection collections, + ICollection collectionDetails, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + foreach (var collectionDetail in collectionDetails) + { + collectionDetail.Manage = true; + } + // Simulate one collection missing the manage permission + collectionDetails.First().Manage = false; + + // Ensure the user is not an owner/admin and does not have edit any collection permission + organization.Type = OrganizationUserType.User; + organization.Permissions.EditAnyCollection = false; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ModifyAccess }, + new ClaimsPrincipal(), + collections + ); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(Arg.Any()).Returns(organization); + sutProvider.GetDependency().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails); + + await sutProvider.Sut.HandleAsync(context); + Assert.False(context.HasSucceeded); + sutProvider.GetDependency().ReceivedWithAnyArgs().GetOrganization(default); + await sutProvider.GetDependency().ReceivedWithAnyArgs() + .GetManyByUserIdAsync(default); + } + [Theory, CollectionCustomization] [BitAutoData(OrganizationUserType.User, false, false, true)] [BitAutoData(OrganizationUserType.Admin, false, true, false)] @@ -123,6 +597,82 @@ public class BulkCollectionAuthorizationHandlerTests Assert.True(context.HasSucceeded); } + [Theory, BitAutoData, CollectionCustomization] + public async Task CanDeleteAsync_WithProviderUser_Success( + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.User; + organization.Permissions.DeleteAnyCollection = false; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Delete }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().ProviderUserForOrgAsync(organization.Id).Returns(true); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData, CollectionCustomization] + public async Task CanDeleteAsync_WhenCustomUserWithRequiredPermissions_Success( + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.Custom; + organization.Permissions.DeleteAnyCollection = true; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Delete }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, CollectionCustomization] + [BitAutoData(OrganizationUserType.User)] + [BitAutoData(OrganizationUserType.Custom)] + public async Task CanDeleteAsync_WhenMissingPermissions_Failure( + OrganizationUserType userType, + SutProvider sutProvider, + ICollection collections, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = userType; + organization.Permissions.DeleteAnyCollection = false; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.Delete }, + new ClaimsPrincipal(), + collections); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.False(context.HasSucceeded); + } + [Theory, BitAutoData, CollectionCustomization] public async Task HandleRequirementAsync_MissingUserId_Failure( SutProvider sutProvider, @@ -184,41 +734,4 @@ public class BulkCollectionAuthorizationHandlerTests await sutProvider.Sut.HandleAsync(context); Assert.True(context.HasFailed); } - - [Theory, BitAutoData, CollectionCustomization] - public async Task CanManageCollectionAccessAsync_MissingManageCollectionPermission_Failure( - SutProvider sutProvider, - ICollection collections, - ICollection collectionDetails, - CurrentContextOrganization organization) - { - var actingUserId = Guid.NewGuid(); - - foreach (var collectionDetail in collectionDetails) - { - collectionDetail.Manage = true; - } - // Simulate one collection missing the manage permission - collectionDetails.First().Manage = false; - - // Ensure the user is not an owner/admin and does not have edit any collection permission - organization.Type = OrganizationUserType.User; - organization.Permissions.EditAnyCollection = false; - - var context = new AuthorizationHandlerContext( - new[] { CollectionOperations.ModifyAccess }, - new ClaimsPrincipal(), - collections - ); - - sutProvider.GetDependency().UserId.Returns(actingUserId); - sutProvider.GetDependency().GetOrganization(Arg.Any()).Returns(organization); - sutProvider.GetDependency().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails); - - await sutProvider.Sut.HandleAsync(context); - Assert.False(context.HasSucceeded); - sutProvider.GetDependency().ReceivedWithAnyArgs().GetOrganization(default); - await sutProvider.GetDependency().ReceivedWithAnyArgs() - .GetManyByUserIdAsync(default); - } } diff --git a/test/Api.Test/Vault/AuthorizationHandlers/CollectionAuthorizationHandlerTests.cs b/test/Api.Test/Vault/AuthorizationHandlers/CollectionAuthorizationHandlerTests.cs index 3a7b2fec60..70c96444d3 100644 --- a/test/Api.Test/Vault/AuthorizationHandlers/CollectionAuthorizationHandlerTests.cs +++ b/test/Api.Test/Vault/AuthorizationHandlers/CollectionAuthorizationHandlerTests.cs @@ -18,32 +18,14 @@ namespace Bit.Api.Test.Vault.AuthorizationHandlers; public class CollectionAuthorizationHandlerTests { [Theory] - [BitAutoData(OrganizationUserType.Admin, false, false, false, false, false, true)] - [BitAutoData(OrganizationUserType.Owner, false, false, false, false, false, true)] - [BitAutoData(OrganizationUserType.User, false, false, false, false, false, false)] - [BitAutoData(OrganizationUserType.Custom, true, false, false, false, false, true)] - [BitAutoData(OrganizationUserType.Custom, false, true, false, false, false, true)] - [BitAutoData(OrganizationUserType.Custom, false, false, true, false, false, true)] - [BitAutoData(OrganizationUserType.Custom, false, false, false, true, false, true)] - [BitAutoData(OrganizationUserType.Custom, false, false, false, false, true, true)] - [BitAutoData(OrganizationUserType.Custom, false, false, false, false, false, false)] - public async Task CanReadAllAccessAsync_ReturnsExpectedResult( - OrganizationUserType userType, bool editAnyCollection, bool deleteAnyCollection, - bool manageGroups, bool manageUsers, bool accessImportExport, bool expectedSuccess, + [BitAutoData(OrganizationUserType.Admin)] + [BitAutoData(OrganizationUserType.Owner)] + public async Task CanReadAllAsync_WhenAdminOrOwner_Success( + OrganizationUserType userType, Guid userId, SutProvider sutProvider, CurrentContextOrganization organization) { - var permissions = new Permissions - { - EditAnyCollection = editAnyCollection, - DeleteAnyCollection = deleteAnyCollection, - ManageGroups = manageGroups, - ManageUsers = manageUsers, - AccessImportExport = accessImportExport - }; - organization.Type = userType; - organization.Permissions = permissions; var context = new AuthorizationHandlerContext( new[] { CollectionOperations.ReadAll(organization.Id) }, @@ -55,14 +37,16 @@ public class CollectionAuthorizationHandlerTests await sutProvider.Sut.HandleAsync(context); - Assert.Equal(expectedSuccess, context.HasSucceeded); + Assert.True(context.HasSucceeded); } [Theory, BitAutoData] - public async Task CanReadAllAccessAsync_WithProviderUser_Success( + public async Task CanReadAllAsync_WhenProviderUser_Success( Guid userId, SutProvider sutProvider, CurrentContextOrganization organization) { + organization.Type = OrganizationUserType.User; + var context = new AuthorizationHandlerContext( new[] { CollectionOperations.ReadAll(organization.Id) }, new ClaimsPrincipal(), @@ -80,6 +64,167 @@ public class CollectionAuthorizationHandlerTests Assert.True(context.HasSucceeded); } + [Theory] + [BitAutoData(true, false, false)] + [BitAutoData(false, true, false)] + [BitAutoData(false, false, true)] + public async Task CanReadAllAsync_WhenCustomUserWithRequiredPermissions_Success( + bool editAnyCollection, bool deleteAnyCollection, bool accessImportExport, + SutProvider sutProvider, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.Custom; + organization.Permissions.EditAnyCollection = editAnyCollection; + organization.Permissions.DeleteAnyCollection = deleteAnyCollection; + organization.Permissions.AccessImportExport = accessImportExport; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAll(organization.Id) }, + new ClaimsPrincipal(), + null); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory] + [BitAutoData(OrganizationUserType.User)] + [BitAutoData(OrganizationUserType.Custom)] + public async Task CanReadAllAsync_WhenMissingAccess_Failure( + OrganizationUserType userType, + SutProvider sutProvider, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = userType; + organization.Permissions.EditAnyCollection = false; + organization.Permissions.DeleteAnyCollection = false; + organization.Permissions.AccessImportExport = false; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAll(organization.Id) }, + new ClaimsPrincipal(), + null); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.False(context.HasSucceeded); + } + + [Theory] + [BitAutoData(OrganizationUserType.Admin)] + [BitAutoData(OrganizationUserType.Owner)] + public async Task CanReadAllWithAccessAsync_WhenAdminOrOwner_Success( + OrganizationUserType userType, + Guid userId, SutProvider sutProvider, + CurrentContextOrganization organization) + { + organization.Type = userType; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAllWithAccess(organization.Id) }, + new ClaimsPrincipal(), + null); + + sutProvider.GetDependency().UserId.Returns(userId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData] + public async Task CanReadAllWithAccessAsync_WhenProviderUser_Success( + Guid userId, + SutProvider sutProvider, CurrentContextOrganization organization) + { + organization.Type = OrganizationUserType.User; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAllWithAccess(organization.Id) }, + new ClaimsPrincipal(), + null); + + sutProvider.GetDependency() + .UserId + .Returns(userId); + sutProvider.GetDependency() + .ProviderUserForOrgAsync(organization.Id) + .Returns(true); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory] + [BitAutoData(true, false, false)] + [BitAutoData(false, true, false)] + [BitAutoData(false, false, true)] + public async Task CanReadAllWithAccessAsync_WhenCustomUserWithRequiredPermissions_Success( + bool editAnyCollection, bool deleteAnyCollection, bool accessImportExport, + SutProvider sutProvider, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = OrganizationUserType.Custom; + organization.Permissions.EditAnyCollection = editAnyCollection; + organization.Permissions.DeleteAnyCollection = deleteAnyCollection; + organization.Permissions.AccessImportExport = accessImportExport; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAllWithAccess(organization.Id) }, + new ClaimsPrincipal(), + null); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory] + [BitAutoData(OrganizationUserType.User)] + [BitAutoData(OrganizationUserType.Custom)] + public async Task CanReadAllWithAccessAsync_WhenMissingAccess_Failure( + OrganizationUserType userType, + SutProvider sutProvider, + CurrentContextOrganization organization) + { + var actingUserId = Guid.NewGuid(); + + organization.Type = userType; + organization.Permissions.EditAnyCollection = false; + organization.Permissions.DeleteAnyCollection = false; + organization.Permissions.AccessImportExport = false; + + var context = new AuthorizationHandlerContext( + new[] { CollectionOperations.ReadAllWithAccess(organization.Id) }, + new ClaimsPrincipal(), + null); + + sutProvider.GetDependency().UserId.Returns(actingUserId); + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.False(context.HasSucceeded); + } + [Theory, BitAutoData] public async Task HandleRequirementAsync_MissingUserId_Failure( Guid organizationId,