1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 16:12:49 -05:00

[AC-1139] Resolved conflict when resolving operations between CollectionOperations and BulkCollectionOperations

This commit is contained in:
Rui Tome
2023-11-25 13:06:45 +00:00
parent 95e6211ab9
commit 0229165849
7 changed files with 844 additions and 196 deletions

View File

@ -198,7 +198,7 @@ public class CollectionsController : Controller
var collection = model.ToCollection(orgId);
var authorized = UseFlexibleCollections
? (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Create)).Succeeded
? (await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.Create)).Succeeded
: await CanCreateCollection(orgId, collection.Id) || await CanEditCollectionAsync(orgId, collection.Id);
if (!authorized)
{
@ -269,7 +269,7 @@ public class CollectionsController : Controller
throw new NotFoundException("One or more collections not found.");
}
var result = await _authorizationService.AuthorizeAsync(User, collections, CollectionOperations.ModifyAccess);
var result = await _authorizationService.AuthorizeAsync(User, collections, BulkCollectionOperations.ModifyAccess);
if (!result.Succeeded)
{
@ -311,7 +311,7 @@ public class CollectionsController : Controller
{
// New flexible collections logic
var collections = await _collectionRepository.GetManyByManyIdsAsync(model.Ids);
var result = await _authorizationService.AuthorizeAsync(User, collections, CollectionOperations.Delete);
var result = await _authorizationService.AuthorizeAsync(User, collections, BulkCollectionOperations.Delete);
if (!result.Succeeded)
{
throw new NotFoundException();
@ -496,7 +496,7 @@ public class CollectionsController : Controller
private async Task<CollectionResponseModel> Get_vNext(Guid collectionId)
{
var collection = await _collectionRepository.GetByIdAsync(collectionId);
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Read)).Succeeded;
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.Read)).Succeeded;
if (!authorized)
{
throw new NotFoundException();
@ -509,7 +509,7 @@ public class CollectionsController : Controller
{
// New flexible collections logic
var (collection, access) = await _collectionRepository.GetByIdWithAccessAsync(id);
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Read)).Succeeded;
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.Read)).Succeeded;
if (!authorized)
{
throw new NotFoundException();
@ -570,7 +570,7 @@ public class CollectionsController : Controller
private async Task<IEnumerable<SelectionReadOnlyResponseModel>> GetUsers_vNext(Guid id)
{
var collection = await _collectionRepository.GetByIdAsync(id);
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.ReadAccess)).Succeeded;
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.ReadAccess)).Succeeded;
if (!authorized)
{
throw new NotFoundException();
@ -584,7 +584,7 @@ public class CollectionsController : Controller
private async Task<CollectionResponseModel> Put_vNext(Guid id, CollectionRequestModel model)
{
var collection = await _collectionRepository.GetByIdAsync(id);
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Update)).Succeeded;
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.Update)).Succeeded;
if (!authorized)
{
throw new NotFoundException();
@ -599,7 +599,7 @@ public class CollectionsController : Controller
private async Task PutUsers_vNext(Guid id, IEnumerable<SelectionReadOnlyRequestModel> model)
{
var collection = await _collectionRepository.GetByIdAsync(id);
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.ModifyAccess)).Succeeded;
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.ModifyAccess)).Succeeded;
if (!authorized)
{
throw new NotFoundException();
@ -611,7 +611,7 @@ public class CollectionsController : Controller
private async Task Delete_vNext(Guid id)
{
var collection = await _collectionRepository.GetByIdAsync(id);
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Delete)).Succeeded;
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.Delete)).Succeeded;
if (!authorized)
{
throw new NotFoundException();
@ -623,7 +623,7 @@ public class CollectionsController : Controller
private async Task DeleteUser_vNext(Guid id, Guid orgUserId)
{
var collection = await _collectionRepository.GetByIdAsync(id);
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.ModifyAccess)).Succeeded;
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.ModifyAccess)).Succeeded;
if (!authorized)
{
throw new NotFoundException();

View File

@ -122,8 +122,8 @@ public static class ServiceCollectionExtensions
public static void AddAuthorizationHandlers(this IServiceCollection services)
{
services.AddScoped<IAuthorizationHandler, CollectionAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, BulkCollectionAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, CollectionAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, GroupAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, OrganizationUserAuthorizationHandler>();
}

View File

@ -15,7 +15,7 @@ namespace Bit.Api.Vault.AuthorizationHandlers.Collections;
/// Handles authorization logic for Collection objects, including access permissions for users and groups.
/// This uses new logic implemented in the Flexible Collections initiative.
/// </summary>
public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<CollectionOperationRequirement, Collection>
public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkCollectionOperationRequirement, Collection>
{
private readonly ICurrentContext _currentContext;
private readonly ICollectionRepository _collectionRepository;
@ -35,7 +35,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<Colle
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
CollectionOperationRequirement requirement, ICollection<Collection>? resources)
BulkCollectionOperationRequirement requirement, ICollection<Collection>? resources)
{
if (!UseFlexibleCollections)
{
@ -69,27 +69,30 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<Colle
switch (requirement)
{
case not null when requirement == CollectionOperations.Create:
case not null when requirement == BulkCollectionOperations.Create:
await CanCreateAsync(context, requirement, org);
break;
case not null when requirement == CollectionOperations.Read:
case not null when requirement == CollectionOperations.ReadAccess:
case not null when requirement == BulkCollectionOperations.Read:
await CanReadAsync(context, requirement, resources, org);
break;
case not null when requirement == CollectionOperations.Delete:
await CanDeleteAsync(context, requirement, resources, org);
case not null when requirement == BulkCollectionOperations.ReadAccess:
await CanReadAccessAsync(context, requirement, resources, org);
break;
case not null when requirement == CollectionOperations.Update:
case not null when requirement == CollectionOperations.ModifyAccess:
case not null when requirement == BulkCollectionOperations.Update:
case not null when requirement == BulkCollectionOperations.ModifyAccess:
await CanManageCollectionAccessAsync(context, requirement, resources, org);
break;
case not null when requirement == BulkCollectionOperations.Delete:
await CanDeleteAsync(context, requirement, resources, org);
break;
}
}
private async Task CanCreateAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
private async Task CanCreateAsync(AuthorizationHandlerContext context, IAuthorizationRequirement requirement,
CurrentContextOrganization? org)
{
// If the limit collection management setting is disabled, allow any user to create collections
@ -110,25 +113,111 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<Colle
}
}
private async Task CanReadAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
ICollection<Collection> targetCollections, CurrentContextOrganization org)
private async Task CanReadAsync(AuthorizationHandlerContext context, IAuthorizationRequirement requirement,
ICollection<Collection> resources, CurrentContextOrganization? org)
{
if (org.Type is OrganizationUserType.Owner or OrganizationUserType.Admin ||
org.Permissions.EditAnyCollection || org.Permissions.DeleteAnyCollection ||
await _currentContext.ProviderUserForOrgAsync(org.Id))
// Owners, Admins, and users with EditAnyCollection or DeleteAnyCollection permission can always read a collection
if (org is
{ LimitCollectionCreationDeletion: false } or
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
{ Permissions.EditAnyCollection: true } or
{ Permissions.DeleteAnyCollection: true } or
{ Permissions.CreateNewCollections: true } or
{ Permissions.ManageUsers: true })
{
context.Succeed(requirement);
return;
}
var canManageCollections = await HasCollectionAccessAsync(targetCollections, org, requireManagePermission: false);
if (canManageCollections)
// The acting user is a member of the target organization,
// ensure they have access for the collection being read
if (org is not null)
{
var canManageCollections = await HasCollectionAccessAsync(resources, org, requireManagePermission: false);
if (canManageCollections)
{
context.Succeed(requirement);
return;
}
}
// Allow provider users to read collections if they are a provider for the target organization
if (await _currentContext.ProviderUserForOrgAsync(_targetOrganizationId))
{
context.Succeed(requirement);
}
}
private async Task CanDeleteAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
private async Task CanReadAccessAsync(AuthorizationHandlerContext context, IAuthorizationRequirement requirement,
ICollection<Collection> resources, CurrentContextOrganization? org)
{
// Owners, Admins, and users with EditAnyCollection or DeleteAnyCollection permission can always read a collection
if (org is
{ LimitCollectionCreationDeletion: false } or
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
{ Permissions.EditAnyCollection: true } or
{ Permissions.DeleteAnyCollection: true } or
{ Permissions.CreateNewCollections: true })
{
context.Succeed(requirement);
return;
}
// The acting user is a member of the target organization,
// ensure they have access for the collection being read
if (org is not null)
{
var canManageCollections = await HasCollectionAccessAsync(resources, org, requireManagePermission: false);
if (canManageCollections)
{
context.Succeed(requirement);
return;
}
}
// Allow provider users to read collections if they are a provider for the target organization
if (await _currentContext.ProviderUserForOrgAsync(_targetOrganizationId))
{
context.Succeed(requirement);
}
}
/// <summary>
/// Ensures the acting user is allowed to manage access permissions for the target collections.
/// </summary>
private async Task CanManageCollectionAccessAsync(AuthorizationHandlerContext context,
IAuthorizationRequirement requirement, ICollection<Collection> resources,
CurrentContextOrganization? org)
{
// Owners, Admins, and users with EditAnyCollection permission can always manage collection access
if (org is
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
{ Permissions.EditAnyCollection: true })
{
context.Succeed(requirement);
return;
}
// The acting user is a member of the target organization,
// ensure they have manage permission for the collection being managed
if (org is not null)
{
var canManageCollections = await HasCollectionAccessAsync(resources, org, requireManagePermission: true);
if (canManageCollections)
{
context.Succeed(requirement);
return;
}
}
// Allow providers to manage collections if they are a provider for the target organization
if (await _currentContext.ProviderUserForOrgAsync(_targetOrganizationId))
{
context.Succeed(requirement);
}
}
private async Task CanDeleteAsync(AuthorizationHandlerContext context, IAuthorizationRequirement requirement,
ICollection<Collection> resources, CurrentContextOrganization? org)
{
// Owners, Admins, and users with DeleteAnyCollection permission can always delete collections
@ -159,41 +248,6 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<Colle
}
}
/// <summary>
/// Ensures the acting user is allowed to manage access permissions for the target collections.
/// </summary>
private async Task CanManageCollectionAccessAsync(AuthorizationHandlerContext context,
IAuthorizationRequirement requirement, ICollection<Collection> resources,
CurrentContextOrganization? org)
{
// Owners, Admins, and users with EditAnyCollection permission can always manage collection access
if (org is
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
{ Permissions.EditAnyCollection: true })
{
context.Succeed(requirement);
return;
}
// The limit collection management setting is disabled,
// ensure acting user has manage permissions for all collections being deleted
if (org is { LimitCollectionCreationDeletion: false })
{
var canManageCollections = await HasCollectionAccessAsync(resources, org, requireManagePermission: true);
if (canManageCollections)
{
context.Succeed(requirement);
return;
}
}
// Allow providers to manage collections if they are a provider for the target organization
if (await _currentContext.ProviderUserForOrgAsync(_targetOrganizationId))
{
context.Succeed(requirement);
}
}
private async Task<bool> HasCollectionAccessAsync(
ICollection<Collection> targetCollections,
CurrentContextOrganization org,

View File

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace Bit.Api.Vault.AuthorizationHandlers.Collections;
public class BulkCollectionOperationRequirement : OperationAuthorizationRequirement { }
public static class BulkCollectionOperations
{
public static readonly BulkCollectionOperationRequirement Create = new() { Name = nameof(Create) };
public static readonly BulkCollectionOperationRequirement Read = new() { Name = nameof(Read) };
public static readonly BulkCollectionOperationRequirement ReadAccess = new() { Name = nameof(ReadAccess) };
public static readonly BulkCollectionOperationRequirement Update = new() { Name = nameof(Update) };
/// <summary>
/// The operation that represents creating, updating, or removing collection access.
/// Combined together to allow for a single requirement to be used for each operation
/// as they all currently share the same underlying authorization logic.
/// </summary>
public static readonly BulkCollectionOperationRequirement ModifyAccess = new() { Name = nameof(ModifyAccess) };
public static readonly BulkCollectionOperationRequirement Delete = new() { Name = nameof(Delete) };
}

View File

@ -6,8 +6,6 @@ public class CollectionOperationRequirement : OperationAuthorizationRequirement
{
public Guid OrganizationId { get; init; }
public CollectionOperationRequirement() { }
public CollectionOperationRequirement(string name, Guid organizationId)
{
Name = name;
@ -17,9 +15,6 @@ public class CollectionOperationRequirement : OperationAuthorizationRequirement
public static class CollectionOperations
{
public static readonly CollectionOperationRequirement Create = new() { Name = nameof(Create) };
public static readonly CollectionOperationRequirement Read = new() { Name = nameof(Read) };
public static readonly CollectionOperationRequirement ReadAccess = new() { Name = nameof(ReadAccess) };
public static CollectionOperationRequirement ReadAll(Guid organizationId)
{
return new CollectionOperationRequirement(nameof(ReadAll), organizationId);
@ -28,12 +23,5 @@ public static class CollectionOperations
{
return new CollectionOperationRequirement(nameof(ReadAllWithAccess), organizationId);
}
public static readonly CollectionOperationRequirement Update = new() { Name = nameof(Update) };
public static readonly CollectionOperationRequirement Delete = new() { Name = nameof(Delete) };
/// <summary>
/// The operation that represents creating, updating, or removing collection access.
/// Combined together to allow for a single requirement to be used for each operation
/// as they all currently share the same underlying authorization logic.
/// </summary>
public static readonly CollectionOperationRequirement ModifyAccess = new() { Name = nameof(ModifyAccess) };
}

View File

@ -35,7 +35,7 @@ public class CollectionsControllerTests
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
ExpectedCollection(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(CollectionOperations.Create)))
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.Create)))
.Returns(AuthorizationResult.Success());
_ = await sutProvider.Sut.Post(orgId, collectionRequest);
@ -61,7 +61,7 @@ public class CollectionsControllerTests
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
collection,
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(CollectionOperations.Update)))
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.Update)))
.Returns(AuthorizationResult.Success());
_ = await sutProvider.Sut.Put(collection.OrganizationId, collection.Id, collectionRequest);
@ -79,7 +79,7 @@ public class CollectionsControllerTests
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
collection,
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(CollectionOperations.Update)))
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.Update)))
.Returns(AuthorizationResult.Failed());
sutProvider.GetDependency<ICollectionRepository>()
@ -160,7 +160,7 @@ public class CollectionsControllerTests
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
collections,
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(CollectionOperations.Delete)))
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.Delete)))
.Returns(AuthorizationResult.Success());
// Act
@ -202,7 +202,7 @@ public class CollectionsControllerTests
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
collections,
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(CollectionOperations.Delete)))
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.Delete)))
.Returns(AuthorizationResult.Failed());
// Assert
@ -237,7 +237,7 @@ public class CollectionsControllerTests
sutProvider.GetDependency<IAuthorizationService>().AuthorizeAsync(
Arg.Any<ClaimsPrincipal>(), ExpectedCollectionAccess(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
r => r.Contains(CollectionOperations.ModifyAccess)
r => r.Contains(BulkCollectionOperations.ModifyAccess)
))
.Returns(AuthorizationResult.Success());
@ -251,7 +251,7 @@ public class CollectionsControllerTests
Arg.Any<ClaimsPrincipal>(),
ExpectedCollectionAccess(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
r => r.Contains(CollectionOperations.ModifyAccess))
r => r.Contains(BulkCollectionOperations.ModifyAccess))
);
await sutProvider.GetDependency<IBulkAddCollectionAccessCommand>().Received()
.AddAccessAsync(
@ -313,7 +313,7 @@ public class CollectionsControllerTests
sutProvider.GetDependency<IAuthorizationService>().AuthorizeAsync(
Arg.Any<ClaimsPrincipal>(), ExpectedCollectionAccess(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
r => r.Contains(CollectionOperations.ModifyAccess)
r => r.Contains(BulkCollectionOperations.ModifyAccess)
))
.Returns(AuthorizationResult.Failed());
@ -324,7 +324,7 @@ public class CollectionsControllerTests
Arg.Any<ClaimsPrincipal>(),
ExpectedCollectionAccess(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
r => r.Contains(CollectionOperations.ModifyAccess))
r => r.Contains(BulkCollectionOperations.ModifyAccess))
);
await sutProvider.GetDependency<IBulkAddCollectionAccessCommand>().DidNotReceiveWithAnyArgs()
.AddAccessAsync(default, default, default);

View File

@ -22,35 +22,25 @@ namespace Bit.Api.Test.Vault.AuthorizationHandlers;
public class BulkCollectionAuthorizationHandlerTests
{
[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 CanManageCollectionAccessAsync_Success(
OrganizationUserType userType, bool editAnyCollection, bool manageCollections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanCreateAsync_WhenAdminOrOwner_Success(
OrganizationUserType userType,
Guid userId, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> collectionDetails,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
foreach (var collectionDetail in collectionDetails)
{
collectionDetail.Manage = manageCollections;
}
organization.Type = userType;
organization.Permissions.EditAnyCollection = editAnyCollection;
organization.LimitCollectionCreationDeletion = true;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ModifyAccess },
new[] { BulkCollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
@ -58,24 +48,25 @@ public class BulkCollectionAuthorizationHandlerTests
}
[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,
[BitAutoData(true, true)]
[BitAutoData(false, false)]
public async Task CanCreateAsync_WhenCustomUserWithRequiredPermissions_Success(
bool createNewCollections, bool limitCollectionCreationDeletion,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions.CreateNewCollections = createNewCollection;
organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete;
organization.Type = OrganizationUserType.Custom;
organization.LimitCollectionCreationDeletion = limitCollectionCreationDeletion;
organization.Permissions = new Permissions
{
CreateNewCollections = createNewCollections
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new[] { BulkCollectionOperations.Create },
new ClaimsPrincipal(),
collections);
@ -88,48 +79,693 @@ public class BulkCollectionAuthorizationHandlerTests
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, false, true)]
[BitAutoData(OrganizationUserType.Admin, false, true, false)]
[BitAutoData(OrganizationUserType.Owner, false, true, false)]
[BitAutoData(OrganizationUserType.Custom, true, true, false)]
public async Task CanDeleteAsync_Success(
OrganizationUserType userType, bool deleteAnyCollection, bool limitCollectionCreateDelete, bool manageCollections,
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanCreateAsync_WhenMissingPermissions_NoSuccess(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> collectionDetails,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
foreach (var collectionDetail in collectionDetails)
{
collectionDetail.Manage = manageCollections;
}
organization.Type = userType;
organization.Permissions.DeleteAnyCollection = deleteAnyCollection;
organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete;
organization.LimitCollectionCreationDeletion = true;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false,
ManageGroups = false,
ManageUsers = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Delete },
new[] { BulkCollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanCreateAsync_WhenMissingOrgAccess_NoSuccess(
Guid userId,
ICollection<Collection> collections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Create },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
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,
Guid userId, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
organization.Type = userType;
organization.LimitCollectionCreationDeletion = true;
organization.Permissions = new Permissions();
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Read },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(true, false, false, false, true)]
[BitAutoData(false, true, false, false, true)]
[BitAutoData(false, false, true, false, true)]
[BitAutoData(false, false, false, true, true)]
[BitAutoData(false, false, false, false, false)]
public async Task CanReadAsync_WhenCustomUserWithRequiredPermissions_Success(
bool manageUsers, bool editAnyCollection, bool deleteAnyCollection,
bool createNewCollections, bool limitCollectionCreationDeletion,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.Custom;
organization.LimitCollectionCreationDeletion = limitCollectionCreationDeletion;
organization.Permissions = new Permissions
{
ManageUsers = manageUsers,
EditAnyCollection = editAnyCollection,
DeleteAnyCollection = deleteAnyCollection,
CreateNewCollections = createNewCollections
};
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Read },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAsync_WhenUserWithRequiredPermissions_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.LimitCollectionCreationDeletion = false;
organization.Permissions = new Permissions();
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Read },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanReadAsync_WhenMissingPermissions_NoSuccess(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.LimitCollectionCreationDeletion = true;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false,
ManageGroups = false,
ManageUsers = false
};
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Read },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAsync_WhenMissingOrgAccess_NoSuccess(
Guid userId,
ICollection<Collection> collections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Read },
new ClaimsPrincipal(),
collections
);
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,
Guid userId, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
organization.Type = userType;
organization.LimitCollectionCreationDeletion = true;
organization.Permissions = new Permissions();
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(true, false, false, true)]
[BitAutoData(false, true, false, true)]
[BitAutoData(false, false, true, true)]
[BitAutoData(false, false, false, false)]
public async Task CanReadAccessAsync_WhenCustomUserWithRequiredPermissions_Success(
bool editAnyCollection, bool deleteAnyCollection,
bool createNewCollections, bool limitCollectionCreationDeletion,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.Custom;
organization.LimitCollectionCreationDeletion = limitCollectionCreationDeletion;
organization.Permissions = new Permissions
{
EditAnyCollection = editAnyCollection,
DeleteAnyCollection = deleteAnyCollection,
CreateNewCollections = createNewCollections
};
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAccessAsync_WhenUserWithRequiredPermissions_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.LimitCollectionCreationDeletion = false;
organization.Permissions = new Permissions();
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanReadAccessAsync_WhenMissingPermissions_NoSuccess(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.LimitCollectionCreationDeletion = true;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false,
ManageGroups = false,
ManageUsers = false
};
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAccessAsync_WhenMissingOrgAccess_NoSuccess(
Guid userId,
ICollection<Collection> collections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections
);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
//
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanManageCollectionAccessAsync_WhenAdminOrOwner_Success(
OrganizationUserType userType,
Guid userId, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
organization.Type = userType;
organization.LimitCollectionCreationDeletion = true;
organization.Permissions = new Permissions();
var operationsToTest = new[]
{
BulkCollectionOperations.Update, BulkCollectionOperations.ModifyAccess
};
foreach (var op in operationsToTest)
{
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
var context = new AuthorizationHandlerContext(
new[] { op },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
// Recreate the SUT to reset the mocks/dependencies between tests
sutProvider.Recreate();
}
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_WithEditAnyCollectionPermission_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.Custom;
organization.Permissions = new Permissions
{
EditAnyCollection = true
};
var operationsToTest = new[]
{
BulkCollectionOperations.Update, BulkCollectionOperations.ModifyAccess
};
foreach (var op in operationsToTest)
{
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
var context = new AuthorizationHandlerContext(
new[] { op },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
// Recreate the SUT to reset the mocks/dependencies between tests
sutProvider.Recreate();
}
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_WithManageCollectionPermission_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
foreach (var c in collections)
{
c.Manage = true;
}
var operationsToTest = new[]
{
BulkCollectionOperations.Update, BulkCollectionOperations.ModifyAccess
};
foreach (var op in operationsToTest)
{
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
var context = new AuthorizationHandlerContext(
new[] { op },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
// Recreate the SUT to reset the mocks/dependencies between tests
sutProvider.Recreate();
}
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanManageCollectionAccessAsync_WhenMissingPermissions_NoSuccess(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.LimitCollectionCreationDeletion = false;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false,
ManageGroups = false,
ManageUsers = false
};
foreach (var collectionDetail in collections)
{
collectionDetail.Manage = true;
}
// Simulate one collection missing the manage permission
collections.First().Manage = false;
var operationsToTest = new[]
{
BulkCollectionOperations.Update, BulkCollectionOperations.ModifyAccess
};
foreach (var op in operationsToTest)
{
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
var context = new AuthorizationHandlerContext(
new[] { op },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
// Recreate the SUT to reset the mocks/dependencies between tests
sutProvider.Recreate();
}
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_WhenMissingOrgAccess_NoSuccess(
Guid userId,
ICollection<Collection> collections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
var operationsToTest = new[]
{
BulkCollectionOperations.Update, BulkCollectionOperations.ModifyAccess
};
foreach (var op in operationsToTest)
{
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
var context = new AuthorizationHandlerContext(
new[] { op },
new ClaimsPrincipal(),
collections
);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
// Recreate the SUT to reset the mocks/dependencies between tests
sutProvider.Recreate();
}
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanDeleteAsync_WhenAdminOrOwner_Success(
OrganizationUserType userType,
Guid userId, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
organization.Type = userType;
organization.LimitCollectionCreationDeletion = true;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WithDeleteAnyCollectionPermission_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.Custom;
organization.LimitCollectionCreationDeletion = false;
organization.Permissions = new Permissions
{
DeleteAnyCollection = true
};
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WithManageCollectionPermission_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
foreach (var c in collections)
{
c.Manage = true;
}
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanDeleteAsync_WhenMissingPermissions_NoSuccess(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.LimitCollectionCreationDeletion = true;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false,
ManageGroups = false,
ManageUsers = false
};
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenMissingOrgAccess_NoSuccess(
Guid userId,
ICollection<Collection> collections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_MissingUserId_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections)
{
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new[] { BulkCollectionOperations.Create },
new ClaimsPrincipal(),
collections
);
@ -153,7 +789,7 @@ public class BulkCollectionAuthorizationHandlerTests
collections.First().OrganizationId = Guid.NewGuid();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new[] { BulkCollectionOperations.Create },
new ClaimsPrincipal(),
collections
);
@ -165,51 +801,38 @@ public class BulkCollectionAuthorizationHandlerTests
sutProvider.GetDependency<ICurrentContext>().DidNotReceiveWithAnyArgs().GetOrganization(default);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_MissingOrg_NoSuccess(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections)
{
var actingUserId = Guid.NewGuid();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_Provider_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections)
{
var actingUserId = Guid.NewGuid();
var orgId = collections.First().OrganizationId;
var operationsToTest = new[]
{
CollectionOperations.Create, CollectionOperations.Delete, CollectionOperations.ModifyAccess
BulkCollectionOperations.Create,
BulkCollectionOperations.Read,
BulkCollectionOperations.ReadAccess,
BulkCollectionOperations.Update,
BulkCollectionOperations.ModifyAccess,
BulkCollectionOperations.Delete,
};
foreach (var op in operationsToTest)
{
var actingUserId = Guid.NewGuid();
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(orgId).Returns((CurrentContextOrganization)null);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(true);
var context = new AuthorizationHandlerContext(
new[] { op },
new ClaimsPrincipal(),
collections
);
var orgId = collections.First().OrganizationId;
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(orgId).Returns((CurrentContextOrganization)null);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
await sutProvider.GetDependency<ICurrentContext>().Received().ProviderUserForOrgAsync(orgId);
@ -217,41 +840,4 @@ public class BulkCollectionAuthorizationHandlerTests
sutProvider.Recreate();
}
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_MissingManageCollectionPermission_NoSuccess(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> 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<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
sutProvider.GetDependency<ICurrentContext>().ReceivedWithAnyArgs().GetOrganization(default);
await sutProvider.GetDependency<ICollectionRepository>().ReceivedWithAnyArgs()
.GetManyByUserIdAsync(default);
}
}