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:
@ -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();
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
|
Reference in New Issue
Block a user