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

[AC-1139] Created BulkCollectionOperations.ReadWithAccess

This commit is contained in:
Rui Tome
2023-11-29 16:29:14 +00:00
parent bad95c50df
commit e7dc0a4b8a
4 changed files with 30 additions and 34 deletions

View File

@ -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, BulkCollectionOperations.Read)).Succeeded; var authorized = (await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.ReadWithAccess)).Succeeded;
if (!authorized) if (!authorized)
{ {
throw new NotFoundException(); throw new NotFoundException();

View File

@ -74,16 +74,17 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
break; break;
case not null when requirement == BulkCollectionOperations.Read: case not null when requirement == BulkCollectionOperations.Read:
case not null when requirement == BulkCollectionOperations.ReadAccess:
await CanReadAsync(context, requirement, resources, org); await CanReadAsync(context, requirement, resources, org);
break; break;
case not null when requirement == BulkCollectionOperations.ReadAccess: case not null when requirement == BulkCollectionOperations.ReadWithAccess:
await CanReadAccessAsync(context, requirement, resources, org); await CanReadWithAccessAsync(context, requirement, resources, org);
break; break;
case not null when requirement == BulkCollectionOperations.Update: case not null when requirement == BulkCollectionOperations.Update:
case not null when requirement == BulkCollectionOperations.ModifyAccess: case not null when requirement == BulkCollectionOperations.ModifyAccess:
await CanManageCollectionAccessAsync(context, requirement, resources, org); await CanUpdateCollection(context, requirement, resources, org);
break; break;
case not null when requirement == BulkCollectionOperations.Delete: case not null when requirement == BulkCollectionOperations.Delete:
@ -122,8 +123,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or { Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
{ Permissions.EditAnyCollection: true } or { Permissions.EditAnyCollection: true } or
{ Permissions.DeleteAnyCollection: true } or { Permissions.DeleteAnyCollection: true } or
{ Permissions.CreateNewCollections: true } or { Permissions.CreateNewCollections: true })
{ Permissions.ManageUsers: true })
{ {
context.Succeed(requirement); context.Succeed(requirement);
return; return;
@ -133,7 +133,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
// ensure they have access for the collection being read // ensure they have access for the collection being read
if (org is not null) if (org is not null)
{ {
var canManageCollections = await HasCollectionAccessAsync(resources, org, requireManagePermission: false); var canManageCollections = await CanManageCollectionsAsync(resources, org);
if (canManageCollections) if (canManageCollections)
{ {
context.Succeed(requirement); context.Succeed(requirement);
@ -148,7 +148,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
} }
} }
private async Task CanReadAccessAsync(AuthorizationHandlerContext context, IAuthorizationRequirement requirement, private async Task CanReadWithAccessAsync(AuthorizationHandlerContext context, IAuthorizationRequirement requirement,
ICollection<Collection> resources, CurrentContextOrganization? org) ICollection<Collection> resources, CurrentContextOrganization? org)
{ {
// Owners, Admins, and users with EditAnyCollection or DeleteAnyCollection permission can always read a collection // Owners, Admins, and users with EditAnyCollection or DeleteAnyCollection permission can always read a collection
@ -157,7 +157,8 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or { Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
{ Permissions.EditAnyCollection: true } or { Permissions.EditAnyCollection: true } or
{ Permissions.DeleteAnyCollection: true } or { Permissions.DeleteAnyCollection: true } or
{ Permissions.CreateNewCollections: true }) { Permissions.CreateNewCollections: true } or
{ Permissions.ManageUsers: true })
{ {
context.Succeed(requirement); context.Succeed(requirement);
return; return;
@ -167,7 +168,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
// ensure they have access for the collection being read // ensure they have access for the collection being read
if (org is not null) if (org is not null)
{ {
var canManageCollections = await HasCollectionAccessAsync(resources, org, requireManagePermission: false); var canManageCollections = await CanManageCollectionsAsync(resources, org);
if (canManageCollections) if (canManageCollections)
{ {
context.Succeed(requirement); context.Succeed(requirement);
@ -183,9 +184,9 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
} }
/// <summary> /// <summary>
/// Ensures the acting user is allowed to manage access permissions for the target collections. /// Ensures the acting user is allowed to update the target collections or manage access permissions for them.
/// </summary> /// </summary>
private async Task CanManageCollectionAccessAsync(AuthorizationHandlerContext context, private async Task CanUpdateCollection(AuthorizationHandlerContext context,
IAuthorizationRequirement requirement, ICollection<Collection> resources, IAuthorizationRequirement requirement, ICollection<Collection> resources,
CurrentContextOrganization? org) CurrentContextOrganization? org)
{ {
@ -202,7 +203,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
// ensure they have manage permission for the collection being managed // ensure they have manage permission for the collection being managed
if (org is not null) if (org is not null)
{ {
var canManageCollections = await HasCollectionAccessAsync(resources, org, requireManagePermission: true); var canManageCollections = await CanManageCollectionsAsync(resources, org);
if (canManageCollections) if (canManageCollections)
{ {
context.Succeed(requirement); context.Succeed(requirement);
@ -233,7 +234,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
// ensure acting user has manage permissions for all collections being deleted // ensure acting user has manage permissions for all collections being deleted
if (org is { LimitCollectionCreationDeletion: false }) if (org is { LimitCollectionCreationDeletion: false })
{ {
var canManageCollections = await HasCollectionAccessAsync(resources, org, requireManagePermission: true); var canManageCollections = await CanManageCollectionsAsync(resources, org);
if (canManageCollections) if (canManageCollections)
{ {
context.Succeed(requirement); context.Succeed(requirement);
@ -248,18 +249,16 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
} }
} }
private async Task<bool> HasCollectionAccessAsync( private async Task<bool> CanManageCollectionsAsync(
ICollection<Collection> targetCollections, ICollection<Collection> targetCollections,
CurrentContextOrganization org, CurrentContextOrganization org)
bool requireManagePermission)
{ {
// List of collection Ids the acting user has access to // List of collection Ids the acting user has access to
var manageableCollectionIds = var manageableCollectionIds =
(await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId!.Value)) (await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId!.Value))
.Where(c => .Where(c =>
// If requireManagePermission is true, check Collections with Manage permission // Check Collections with Manage permission
(!requireManagePermission || c.Manage) c.Manage && c.OrganizationId == org.Id)
&& c.OrganizationId == org.Id)
.Select(c => c.Id) .Select(c => c.Id)
.ToHashSet(); .ToHashSet();

View File

@ -9,6 +9,7 @@ public static class BulkCollectionOperations
public static readonly BulkCollectionOperationRequirement Create = new() { Name = nameof(Create) }; public static readonly BulkCollectionOperationRequirement Create = new() { Name = nameof(Create) };
public static readonly BulkCollectionOperationRequirement Read = new() { Name = nameof(Read) }; public static readonly BulkCollectionOperationRequirement Read = new() { Name = nameof(Read) };
public static readonly BulkCollectionOperationRequirement ReadAccess = new() { Name = nameof(ReadAccess) }; public static readonly BulkCollectionOperationRequirement ReadAccess = new() { Name = nameof(ReadAccess) };
public static readonly BulkCollectionOperationRequirement ReadWithAccess = new() { Name = nameof(ReadWithAccess) };
public static readonly BulkCollectionOperationRequirement Update = new() { Name = nameof(Update) }; public static readonly BulkCollectionOperationRequirement Update = new() { Name = nameof(Update) };
/// <summary> /// <summary>
/// The operation that represents creating, updating, or removing collection access. /// The operation that represents creating, updating, or removing collection access.

View File

@ -158,14 +158,13 @@ public class BulkCollectionAuthorizationHandlerTests
} }
[Theory, CollectionCustomization] [Theory, CollectionCustomization]
[BitAutoData(true, false, false, false, true)] [BitAutoData(true, false, false, true)]
[BitAutoData(false, true, false, false, true)] [BitAutoData(false, true, false, true)]
[BitAutoData(false, false, true, false, true)] [BitAutoData(false, false, true, true)]
[BitAutoData(false, false, false, true, true)] [BitAutoData(false, false, false, false)]
[BitAutoData(false, false, false, false, false)]
public async Task CanReadAsync_WhenCustomUserWithRequiredPermissions_Success( public async Task CanReadAsync_WhenCustomUserWithRequiredPermissions_Success(
bool manageUsers, bool editAnyCollection, bool deleteAnyCollection, bool editAnyCollection, bool deleteAnyCollection,
bool createNewCollections, bool limitCollectionCreationDeletion, bool createNewCollections, bool limitCollectionCreationDeletion,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections, ICollection<Collection> collections,
@ -177,7 +176,6 @@ public class BulkCollectionAuthorizationHandlerTests
organization.LimitCollectionCreationDeletion = limitCollectionCreationDeletion; organization.LimitCollectionCreationDeletion = limitCollectionCreationDeletion;
organization.Permissions = new Permissions organization.Permissions = new Permissions
{ {
ManageUsers = manageUsers,
EditAnyCollection = editAnyCollection, EditAnyCollection = editAnyCollection,
DeleteAnyCollection = deleteAnyCollection, DeleteAnyCollection = deleteAnyCollection,
CreateNewCollections = createNewCollections CreateNewCollections = createNewCollections
@ -421,12 +419,10 @@ public class BulkCollectionAuthorizationHandlerTests
Assert.False(context.HasSucceeded); Assert.False(context.HasSucceeded);
} }
//
[Theory, CollectionCustomization] [Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)] [BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)] [BitAutoData(OrganizationUserType.Owner)]
public async Task CanManageCollectionAccessAsync_WhenAdminOrOwner_Success( public async Task CanUpdateCollection_WhenAdminOrOwner_Success(
OrganizationUserType userType, OrganizationUserType userType,
Guid userId, SutProvider<BulkCollectionAuthorizationHandler> sutProvider, Guid userId, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections, ICollection<Collection> collections,
@ -461,7 +457,7 @@ public class BulkCollectionAuthorizationHandlerTests
} }
[Theory, BitAutoData, CollectionCustomization] [Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_WithEditAnyCollectionPermission_Success( public async Task CanUpdateCollection_WithEditAnyCollectionPermission_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections, ICollection<Collection> collections,
CurrentContextOrganization organization) CurrentContextOrganization organization)
@ -499,7 +495,7 @@ public class BulkCollectionAuthorizationHandlerTests
} }
[Theory, BitAutoData, CollectionCustomization] [Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_WithManageCollectionPermission_Success( public async Task CanUpdateCollection_WithManageCollectionPermission_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections, ICollection<CollectionDetails> collections,
CurrentContextOrganization organization) CurrentContextOrganization organization)
@ -542,7 +538,7 @@ public class BulkCollectionAuthorizationHandlerTests
[Theory, CollectionCustomization] [Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)] [BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)] [BitAutoData(OrganizationUserType.Custom)]
public async Task CanManageCollectionAccessAsync_WhenMissingPermissions_NoSuccess( public async Task CanUpdateCollection_WhenMissingPermissions_NoSuccess(
OrganizationUserType userType, OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider, SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections, ICollection<CollectionDetails> collections,
@ -592,7 +588,7 @@ public class BulkCollectionAuthorizationHandlerTests
} }
[Theory, BitAutoData, CollectionCustomization] [Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_WhenMissingOrgAccess_NoSuccess( public async Task CanUpdateCollection_WhenMissingOrgAccess_NoSuccess(
Guid userId, Guid userId,
ICollection<Collection> collections, ICollection<Collection> collections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider) SutProvider<BulkCollectionAuthorizationHandler> sutProvider)