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:
@ -198,7 +198,7 @@ public class CollectionsController : Controller
|
|||||||
var collection = model.ToCollection(orgId);
|
var collection = model.ToCollection(orgId);
|
||||||
|
|
||||||
var authorized = UseFlexibleCollections
|
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);
|
: await CanCreateCollection(orgId, collection.Id) || await CanEditCollectionAsync(orgId, collection.Id);
|
||||||
if (!authorized)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
@ -269,7 +269,7 @@ public class CollectionsController : Controller
|
|||||||
throw new NotFoundException("One or more collections not found.");
|
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)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
@ -311,7 +311,7 @@ public class CollectionsController : Controller
|
|||||||
{
|
{
|
||||||
// New flexible collections logic
|
// New flexible collections logic
|
||||||
var collections = await _collectionRepository.GetManyByManyIdsAsync(model.Ids);
|
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)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -496,7 +496,7 @@ public class CollectionsController : Controller
|
|||||||
private async Task<CollectionResponseModel> Get_vNext(Guid collectionId)
|
private async Task<CollectionResponseModel> Get_vNext(Guid collectionId)
|
||||||
{
|
{
|
||||||
var collection = await _collectionRepository.GetByIdAsync(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)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -509,7 +509,7 @@ public class CollectionsController : Controller
|
|||||||
{
|
{
|
||||||
// New flexible collections logic
|
// New flexible collections logic
|
||||||
var (collection, access) = await _collectionRepository.GetByIdWithAccessAsync(id);
|
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)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -570,7 +570,7 @@ public class CollectionsController : Controller
|
|||||||
private async Task<IEnumerable<SelectionReadOnlyResponseModel>> GetUsers_vNext(Guid id)
|
private async Task<IEnumerable<SelectionReadOnlyResponseModel>> GetUsers_vNext(Guid id)
|
||||||
{
|
{
|
||||||
var collection = await _collectionRepository.GetByIdAsync(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)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -584,7 +584,7 @@ public class CollectionsController : Controller
|
|||||||
private async Task<CollectionResponseModel> Put_vNext(Guid id, CollectionRequestModel model)
|
private async Task<CollectionResponseModel> Put_vNext(Guid id, CollectionRequestModel model)
|
||||||
{
|
{
|
||||||
var collection = await _collectionRepository.GetByIdAsync(id);
|
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)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -599,7 +599,7 @@ public class CollectionsController : Controller
|
|||||||
private async Task PutUsers_vNext(Guid id, IEnumerable<SelectionReadOnlyRequestModel> model)
|
private async Task PutUsers_vNext(Guid id, IEnumerable<SelectionReadOnlyRequestModel> model)
|
||||||
{
|
{
|
||||||
var collection = await _collectionRepository.GetByIdAsync(id);
|
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)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -611,7 +611,7 @@ public class CollectionsController : Controller
|
|||||||
private async Task Delete_vNext(Guid id)
|
private async Task Delete_vNext(Guid id)
|
||||||
{
|
{
|
||||||
var collection = await _collectionRepository.GetByIdAsync(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)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -623,7 +623,7 @@ public class CollectionsController : Controller
|
|||||||
private async Task DeleteUser_vNext(Guid id, Guid orgUserId)
|
private async Task DeleteUser_vNext(Guid id, Guid orgUserId)
|
||||||
{
|
{
|
||||||
var collection = await _collectionRepository.GetByIdAsync(id);
|
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)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
|
@ -122,8 +122,8 @@ public static class ServiceCollectionExtensions
|
|||||||
|
|
||||||
public static void AddAuthorizationHandlers(this IServiceCollection services)
|
public static void AddAuthorizationHandlers(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddScoped<IAuthorizationHandler, CollectionAuthorizationHandler>();
|
|
||||||
services.AddScoped<IAuthorizationHandler, BulkCollectionAuthorizationHandler>();
|
services.AddScoped<IAuthorizationHandler, BulkCollectionAuthorizationHandler>();
|
||||||
|
services.AddScoped<IAuthorizationHandler, CollectionAuthorizationHandler>();
|
||||||
services.AddScoped<IAuthorizationHandler, GroupAuthorizationHandler>();
|
services.AddScoped<IAuthorizationHandler, GroupAuthorizationHandler>();
|
||||||
services.AddScoped<IAuthorizationHandler, OrganizationUserAuthorizationHandler>();
|
services.AddScoped<IAuthorizationHandler, OrganizationUserAuthorizationHandler>();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ namespace Bit.Api.Vault.AuthorizationHandlers.Collections;
|
|||||||
/// Handles authorization logic for Collection objects, including access permissions for users and groups.
|
/// Handles authorization logic for Collection objects, including access permissions for users and groups.
|
||||||
/// This uses new logic implemented in the Flexible Collections initiative.
|
/// This uses new logic implemented in the Flexible Collections initiative.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<CollectionOperationRequirement, Collection>
|
public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkCollectionOperationRequirement, Collection>
|
||||||
{
|
{
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly ICollectionRepository _collectionRepository;
|
private readonly ICollectionRepository _collectionRepository;
|
||||||
@ -35,7 +35,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<Colle
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
||||||
CollectionOperationRequirement requirement, ICollection<Collection>? resources)
|
BulkCollectionOperationRequirement requirement, ICollection<Collection>? resources)
|
||||||
{
|
{
|
||||||
if (!UseFlexibleCollections)
|
if (!UseFlexibleCollections)
|
||||||
{
|
{
|
||||||
@ -69,27 +69,30 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<Colle
|
|||||||
|
|
||||||
switch (requirement)
|
switch (requirement)
|
||||||
{
|
{
|
||||||
case not null when requirement == CollectionOperations.Create:
|
case not null when requirement == BulkCollectionOperations.Create:
|
||||||
await CanCreateAsync(context, requirement, org);
|
await CanCreateAsync(context, requirement, org);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case not null when requirement == CollectionOperations.Read:
|
case not null when requirement == BulkCollectionOperations.Read:
|
||||||
case not null when requirement == CollectionOperations.ReadAccess:
|
|
||||||
await CanReadAsync(context, requirement, resources, org);
|
await CanReadAsync(context, requirement, resources, org);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case not null when requirement == CollectionOperations.Delete:
|
case not null when requirement == BulkCollectionOperations.ReadAccess:
|
||||||
await CanDeleteAsync(context, requirement, resources, org);
|
await CanReadAccessAsync(context, requirement, resources, org);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case not null when requirement == CollectionOperations.Update:
|
case not null when requirement == BulkCollectionOperations.Update:
|
||||||
case not null when requirement == CollectionOperations.ModifyAccess:
|
case not null when requirement == BulkCollectionOperations.ModifyAccess:
|
||||||
await CanManageCollectionAccessAsync(context, requirement, resources, org);
|
await CanManageCollectionAccessAsync(context, requirement, resources, org);
|
||||||
break;
|
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)
|
CurrentContextOrganization? org)
|
||||||
{
|
{
|
||||||
// If the limit collection management setting is disabled, allow any user to create collections
|
// 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,
|
private async Task CanReadAsync(AuthorizationHandlerContext context, IAuthorizationRequirement requirement,
|
||||||
ICollection<Collection> targetCollections, CurrentContextOrganization org)
|
ICollection<Collection> resources, CurrentContextOrganization? org)
|
||||||
{
|
{
|
||||||
if (org.Type is OrganizationUserType.Owner or OrganizationUserType.Admin ||
|
// Owners, Admins, and users with EditAnyCollection or DeleteAnyCollection permission can always read a collection
|
||||||
org.Permissions.EditAnyCollection || org.Permissions.DeleteAnyCollection ||
|
if (org is
|
||||||
await _currentContext.ProviderUserForOrgAsync(org.Id))
|
{ 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);
|
context.Succeed(requirement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var canManageCollections = await HasCollectionAccessAsync(targetCollections, org, requireManagePermission: false);
|
// The acting user is a member of the target organization,
|
||||||
if (canManageCollections)
|
// 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);
|
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)
|
ICollection<Collection> resources, CurrentContextOrganization? org)
|
||||||
{
|
{
|
||||||
// Owners, Admins, and users with DeleteAnyCollection permission can always delete collections
|
// 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(
|
private async Task<bool> HasCollectionAccessAsync(
|
||||||
ICollection<Collection> targetCollections,
|
ICollection<Collection> targetCollections,
|
||||||
CurrentContextOrganization org,
|
CurrentContextOrganization org,
|
||||||
|
@ -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) };
|
||||||
|
}
|
@ -6,8 +6,6 @@ public class CollectionOperationRequirement : OperationAuthorizationRequirement
|
|||||||
{
|
{
|
||||||
public Guid OrganizationId { get; init; }
|
public Guid OrganizationId { get; init; }
|
||||||
|
|
||||||
public CollectionOperationRequirement() { }
|
|
||||||
|
|
||||||
public CollectionOperationRequirement(string name, Guid organizationId)
|
public CollectionOperationRequirement(string name, Guid organizationId)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
@ -17,9 +15,6 @@ public class CollectionOperationRequirement : OperationAuthorizationRequirement
|
|||||||
|
|
||||||
public static class CollectionOperations
|
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)
|
public static CollectionOperationRequirement ReadAll(Guid organizationId)
|
||||||
{
|
{
|
||||||
return new CollectionOperationRequirement(nameof(ReadAll), organizationId);
|
return new CollectionOperationRequirement(nameof(ReadAll), organizationId);
|
||||||
@ -28,12 +23,5 @@ public static class CollectionOperations
|
|||||||
{
|
{
|
||||||
return new CollectionOperationRequirement(nameof(ReadAllWithAccess), organizationId);
|
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) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ public class CollectionsControllerTests
|
|||||||
sutProvider.GetDependency<IAuthorizationService>()
|
sutProvider.GetDependency<IAuthorizationService>()
|
||||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
||||||
ExpectedCollection(),
|
ExpectedCollection(),
|
||||||
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(CollectionOperations.Create)))
|
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.Create)))
|
||||||
.Returns(AuthorizationResult.Success());
|
.Returns(AuthorizationResult.Success());
|
||||||
|
|
||||||
_ = await sutProvider.Sut.Post(orgId, collectionRequest);
|
_ = await sutProvider.Sut.Post(orgId, collectionRequest);
|
||||||
@ -61,7 +61,7 @@ public class CollectionsControllerTests
|
|||||||
sutProvider.GetDependency<IAuthorizationService>()
|
sutProvider.GetDependency<IAuthorizationService>()
|
||||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
||||||
collection,
|
collection,
|
||||||
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(CollectionOperations.Update)))
|
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.Update)))
|
||||||
.Returns(AuthorizationResult.Success());
|
.Returns(AuthorizationResult.Success());
|
||||||
|
|
||||||
_ = await sutProvider.Sut.Put(collection.OrganizationId, collection.Id, collectionRequest);
|
_ = await sutProvider.Sut.Put(collection.OrganizationId, collection.Id, collectionRequest);
|
||||||
@ -79,7 +79,7 @@ public class CollectionsControllerTests
|
|||||||
sutProvider.GetDependency<IAuthorizationService>()
|
sutProvider.GetDependency<IAuthorizationService>()
|
||||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
||||||
collection,
|
collection,
|
||||||
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(CollectionOperations.Update)))
|
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.Update)))
|
||||||
.Returns(AuthorizationResult.Failed());
|
.Returns(AuthorizationResult.Failed());
|
||||||
|
|
||||||
sutProvider.GetDependency<ICollectionRepository>()
|
sutProvider.GetDependency<ICollectionRepository>()
|
||||||
@ -160,7 +160,7 @@ public class CollectionsControllerTests
|
|||||||
sutProvider.GetDependency<IAuthorizationService>()
|
sutProvider.GetDependency<IAuthorizationService>()
|
||||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
||||||
collections,
|
collections,
|
||||||
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(CollectionOperations.Delete)))
|
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.Delete)))
|
||||||
.Returns(AuthorizationResult.Success());
|
.Returns(AuthorizationResult.Success());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@ -202,7 +202,7 @@ public class CollectionsControllerTests
|
|||||||
sutProvider.GetDependency<IAuthorizationService>()
|
sutProvider.GetDependency<IAuthorizationService>()
|
||||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
||||||
collections,
|
collections,
|
||||||
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(CollectionOperations.Delete)))
|
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.Delete)))
|
||||||
.Returns(AuthorizationResult.Failed());
|
.Returns(AuthorizationResult.Failed());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@ -237,7 +237,7 @@ public class CollectionsControllerTests
|
|||||||
sutProvider.GetDependency<IAuthorizationService>().AuthorizeAsync(
|
sutProvider.GetDependency<IAuthorizationService>().AuthorizeAsync(
|
||||||
Arg.Any<ClaimsPrincipal>(), ExpectedCollectionAccess(),
|
Arg.Any<ClaimsPrincipal>(), ExpectedCollectionAccess(),
|
||||||
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
|
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
|
||||||
r => r.Contains(CollectionOperations.ModifyAccess)
|
r => r.Contains(BulkCollectionOperations.ModifyAccess)
|
||||||
))
|
))
|
||||||
.Returns(AuthorizationResult.Success());
|
.Returns(AuthorizationResult.Success());
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ public class CollectionsControllerTests
|
|||||||
Arg.Any<ClaimsPrincipal>(),
|
Arg.Any<ClaimsPrincipal>(),
|
||||||
ExpectedCollectionAccess(),
|
ExpectedCollectionAccess(),
|
||||||
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
|
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
|
||||||
r => r.Contains(CollectionOperations.ModifyAccess))
|
r => r.Contains(BulkCollectionOperations.ModifyAccess))
|
||||||
);
|
);
|
||||||
await sutProvider.GetDependency<IBulkAddCollectionAccessCommand>().Received()
|
await sutProvider.GetDependency<IBulkAddCollectionAccessCommand>().Received()
|
||||||
.AddAccessAsync(
|
.AddAccessAsync(
|
||||||
@ -313,7 +313,7 @@ public class CollectionsControllerTests
|
|||||||
sutProvider.GetDependency<IAuthorizationService>().AuthorizeAsync(
|
sutProvider.GetDependency<IAuthorizationService>().AuthorizeAsync(
|
||||||
Arg.Any<ClaimsPrincipal>(), ExpectedCollectionAccess(),
|
Arg.Any<ClaimsPrincipal>(), ExpectedCollectionAccess(),
|
||||||
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
|
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
|
||||||
r => r.Contains(CollectionOperations.ModifyAccess)
|
r => r.Contains(BulkCollectionOperations.ModifyAccess)
|
||||||
))
|
))
|
||||||
.Returns(AuthorizationResult.Failed());
|
.Returns(AuthorizationResult.Failed());
|
||||||
|
|
||||||
@ -324,7 +324,7 @@ public class CollectionsControllerTests
|
|||||||
Arg.Any<ClaimsPrincipal>(),
|
Arg.Any<ClaimsPrincipal>(),
|
||||||
ExpectedCollectionAccess(),
|
ExpectedCollectionAccess(),
|
||||||
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
|
Arg.Is<IEnumerable<IAuthorizationRequirement>>(
|
||||||
r => r.Contains(CollectionOperations.ModifyAccess))
|
r => r.Contains(BulkCollectionOperations.ModifyAccess))
|
||||||
);
|
);
|
||||||
await sutProvider.GetDependency<IBulkAddCollectionAccessCommand>().DidNotReceiveWithAnyArgs()
|
await sutProvider.GetDependency<IBulkAddCollectionAccessCommand>().DidNotReceiveWithAnyArgs()
|
||||||
.AddAccessAsync(default, default, default);
|
.AddAccessAsync(default, default, default);
|
||||||
|
@ -22,35 +22,25 @@ namespace Bit.Api.Test.Vault.AuthorizationHandlers;
|
|||||||
public class BulkCollectionAuthorizationHandlerTests
|
public class BulkCollectionAuthorizationHandlerTests
|
||||||
{
|
{
|
||||||
[Theory, CollectionCustomization]
|
[Theory, CollectionCustomization]
|
||||||
[BitAutoData(OrganizationUserType.User, false, true)]
|
[BitAutoData(OrganizationUserType.Admin)]
|
||||||
[BitAutoData(OrganizationUserType.Admin, false, false)]
|
[BitAutoData(OrganizationUserType.Owner)]
|
||||||
[BitAutoData(OrganizationUserType.Owner, false, false)]
|
public async Task CanCreateAsync_WhenAdminOrOwner_Success(
|
||||||
[BitAutoData(OrganizationUserType.Custom, true, false)]
|
OrganizationUserType userType,
|
||||||
[BitAutoData(OrganizationUserType.Owner, true, true)]
|
Guid userId, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
|
||||||
public async Task CanManageCollectionAccessAsync_Success(
|
|
||||||
OrganizationUserType userType, bool editAnyCollection, bool manageCollections,
|
|
||||||
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
|
|
||||||
ICollection<Collection> collections,
|
ICollection<Collection> collections,
|
||||||
ICollection<CollectionDetails> collectionDetails,
|
|
||||||
CurrentContextOrganization organization)
|
CurrentContextOrganization organization)
|
||||||
{
|
{
|
||||||
var actingUserId = Guid.NewGuid();
|
|
||||||
foreach (var collectionDetail in collectionDetails)
|
|
||||||
{
|
|
||||||
collectionDetail.Manage = manageCollections;
|
|
||||||
}
|
|
||||||
|
|
||||||
organization.Type = userType;
|
organization.Type = userType;
|
||||||
organization.Permissions.EditAnyCollection = editAnyCollection;
|
organization.LimitCollectionCreationDeletion = true;
|
||||||
|
organization.Permissions = new Permissions();
|
||||||
|
|
||||||
var context = new AuthorizationHandlerContext(
|
var context = new AuthorizationHandlerContext(
|
||||||
new[] { CollectionOperations.ModifyAccess },
|
new[] { BulkCollectionOperations.Create },
|
||||||
new ClaimsPrincipal(),
|
new ClaimsPrincipal(),
|
||||||
collections);
|
collections);
|
||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
|
||||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
|
|
||||||
|
|
||||||
await sutProvider.Sut.HandleAsync(context);
|
await sutProvider.Sut.HandleAsync(context);
|
||||||
|
|
||||||
@ -58,24 +48,25 @@ public class BulkCollectionAuthorizationHandlerTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, CollectionCustomization]
|
[Theory, CollectionCustomization]
|
||||||
[BitAutoData(OrganizationUserType.User, false, false)]
|
[BitAutoData(true, true)]
|
||||||
[BitAutoData(OrganizationUserType.Admin, false, true)]
|
[BitAutoData(false, false)]
|
||||||
[BitAutoData(OrganizationUserType.Owner, false, true)]
|
public async Task CanCreateAsync_WhenCustomUserWithRequiredPermissions_Success(
|
||||||
[BitAutoData(OrganizationUserType.Custom, true, true)]
|
bool createNewCollections, bool limitCollectionCreationDeletion,
|
||||||
public async Task CanCreateAsync_Success(
|
|
||||||
OrganizationUserType userType, bool createNewCollection, bool limitCollectionCreateDelete,
|
|
||||||
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
|
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
|
||||||
ICollection<Collection> collections,
|
ICollection<Collection> collections,
|
||||||
CurrentContextOrganization organization)
|
CurrentContextOrganization organization)
|
||||||
{
|
{
|
||||||
var actingUserId = Guid.NewGuid();
|
var actingUserId = Guid.NewGuid();
|
||||||
|
|
||||||
organization.Type = userType;
|
organization.Type = OrganizationUserType.Custom;
|
||||||
organization.Permissions.CreateNewCollections = createNewCollection;
|
organization.LimitCollectionCreationDeletion = limitCollectionCreationDeletion;
|
||||||
organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete;
|
organization.Permissions = new Permissions
|
||||||
|
{
|
||||||
|
CreateNewCollections = createNewCollections
|
||||||
|
};
|
||||||
|
|
||||||
var context = new AuthorizationHandlerContext(
|
var context = new AuthorizationHandlerContext(
|
||||||
new[] { CollectionOperations.Create },
|
new[] { BulkCollectionOperations.Create },
|
||||||
new ClaimsPrincipal(),
|
new ClaimsPrincipal(),
|
||||||
collections);
|
collections);
|
||||||
|
|
||||||
@ -88,48 +79,693 @@ public class BulkCollectionAuthorizationHandlerTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, CollectionCustomization]
|
[Theory, CollectionCustomization]
|
||||||
[BitAutoData(OrganizationUserType.User, false, false, true)]
|
[BitAutoData(OrganizationUserType.User)]
|
||||||
[BitAutoData(OrganizationUserType.Admin, false, true, false)]
|
[BitAutoData(OrganizationUserType.Custom)]
|
||||||
[BitAutoData(OrganizationUserType.Owner, false, true, false)]
|
public async Task CanCreateAsync_WhenMissingPermissions_NoSuccess(
|
||||||
[BitAutoData(OrganizationUserType.Custom, true, true, false)]
|
OrganizationUserType userType,
|
||||||
public async Task CanDeleteAsync_Success(
|
|
||||||
OrganizationUserType userType, bool deleteAnyCollection, bool limitCollectionCreateDelete, bool manageCollections,
|
|
||||||
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
|
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
|
||||||
ICollection<Collection> collections,
|
ICollection<Collection> collections,
|
||||||
ICollection<CollectionDetails> collectionDetails,
|
|
||||||
CurrentContextOrganization organization)
|
CurrentContextOrganization organization)
|
||||||
{
|
{
|
||||||
var actingUserId = Guid.NewGuid();
|
var actingUserId = Guid.NewGuid();
|
||||||
foreach (var collectionDetail in collectionDetails)
|
|
||||||
{
|
|
||||||
collectionDetail.Manage = manageCollections;
|
|
||||||
}
|
|
||||||
|
|
||||||
organization.Type = userType;
|
organization.Type = userType;
|
||||||
organization.Permissions.DeleteAnyCollection = deleteAnyCollection;
|
organization.LimitCollectionCreationDeletion = true;
|
||||||
organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete;
|
organization.Permissions = new Permissions
|
||||||
|
{
|
||||||
|
EditAnyCollection = false,
|
||||||
|
DeleteAnyCollection = false,
|
||||||
|
ManageGroups = false,
|
||||||
|
ManageUsers = false
|
||||||
|
};
|
||||||
|
|
||||||
var context = new AuthorizationHandlerContext(
|
var context = new AuthorizationHandlerContext(
|
||||||
new[] { CollectionOperations.Delete },
|
new[] { BulkCollectionOperations.Create },
|
||||||
new ClaimsPrincipal(),
|
new ClaimsPrincipal(),
|
||||||
collections);
|
collections);
|
||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
||||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
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);
|
await sutProvider.Sut.HandleAsync(context);
|
||||||
|
|
||||||
Assert.True(context.HasSucceeded);
|
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]
|
[Theory, BitAutoData, CollectionCustomization]
|
||||||
public async Task HandleRequirementAsync_MissingUserId_Failure(
|
public async Task HandleRequirementAsync_MissingUserId_Failure(
|
||||||
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
|
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
|
||||||
ICollection<Collection> collections)
|
ICollection<Collection> collections)
|
||||||
{
|
{
|
||||||
var context = new AuthorizationHandlerContext(
|
var context = new AuthorizationHandlerContext(
|
||||||
new[] { CollectionOperations.Create },
|
new[] { BulkCollectionOperations.Create },
|
||||||
new ClaimsPrincipal(),
|
new ClaimsPrincipal(),
|
||||||
collections
|
collections
|
||||||
);
|
);
|
||||||
@ -153,7 +789,7 @@ public class BulkCollectionAuthorizationHandlerTests
|
|||||||
collections.First().OrganizationId = Guid.NewGuid();
|
collections.First().OrganizationId = Guid.NewGuid();
|
||||||
|
|
||||||
var context = new AuthorizationHandlerContext(
|
var context = new AuthorizationHandlerContext(
|
||||||
new[] { CollectionOperations.Create },
|
new[] { BulkCollectionOperations.Create },
|
||||||
new ClaimsPrincipal(),
|
new ClaimsPrincipal(),
|
||||||
collections
|
collections
|
||||||
);
|
);
|
||||||
@ -165,51 +801,38 @@ public class BulkCollectionAuthorizationHandlerTests
|
|||||||
sutProvider.GetDependency<ICurrentContext>().DidNotReceiveWithAnyArgs().GetOrganization(default);
|
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]
|
[Theory, BitAutoData, CollectionCustomization]
|
||||||
public async Task HandleRequirementAsync_Provider_Success(
|
public async Task HandleRequirementAsync_Provider_Success(
|
||||||
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
|
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
|
||||||
ICollection<Collection> collections)
|
ICollection<Collection> collections)
|
||||||
{
|
{
|
||||||
|
var actingUserId = Guid.NewGuid();
|
||||||
|
var orgId = collections.First().OrganizationId;
|
||||||
|
|
||||||
var operationsToTest = new[]
|
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)
|
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(
|
var context = new AuthorizationHandlerContext(
|
||||||
new[] { op },
|
new[] { op },
|
||||||
new ClaimsPrincipal(),
|
new ClaimsPrincipal(),
|
||||||
collections
|
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);
|
await sutProvider.Sut.HandleAsync(context);
|
||||||
|
|
||||||
Assert.True(context.HasSucceeded);
|
Assert.True(context.HasSucceeded);
|
||||||
await sutProvider.GetDependency<ICurrentContext>().Received().ProviderUserForOrgAsync(orgId);
|
await sutProvider.GetDependency<ICurrentContext>().Received().ProviderUserForOrgAsync(orgId);
|
||||||
|
|
||||||
@ -217,41 +840,4 @@ public class BulkCollectionAuthorizationHandlerTests
|
|||||||
sutProvider.Recreate();
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user