1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-03 09:02:48 -05:00

[AC-1139] Updated CollectionsController GetManyWithDetails and Get to check for flexible collections flag

This commit is contained in:
Rui Tome
2023-10-19 20:58:01 +01:00
parent 8c1a3a6e2d
commit 1e2908ba5e
4 changed files with 51 additions and 4 deletions

View File

@ -95,7 +95,9 @@ public class CollectionsController : Controller
[HttpGet("details")] [HttpGet("details")]
public async Task<ListResponseModel<CollectionAccessDetailsResponseModel>> GetManyWithDetails(Guid orgId) public async Task<ListResponseModel<CollectionAccessDetailsResponseModel>> GetManyWithDetails(Guid orgId)
{ {
if (!await ViewAtLeastOneCollectionAsync(orgId) && !await _currentContext.ManageUsers(orgId) && if (!FlexibleCollectionsIsEnabled &&
!await ViewAtLeastOneCollectionAsync(orgId) &&
!await _currentContext.ManageUsers(orgId) &&
!await _currentContext.ManageGroups(orgId)) !await _currentContext.ManageGroups(orgId))
{ {
throw new NotFoundException(); throw new NotFoundException();
@ -130,7 +132,22 @@ public class CollectionsController : Controller
[HttpGet("")] [HttpGet("")]
public async Task<ListResponseModel<CollectionResponseModel>> Get(Guid orgId) public async Task<ListResponseModel<CollectionResponseModel>> Get(Guid orgId)
{ {
IEnumerable<Collection> orgCollections = await _collectionService.GetOrganizationCollectionsAsync(orgId); IEnumerable<Collection> orgCollections;
if (FlexibleCollectionsIsEnabled)
{
orgCollections = await _collectionRepository.GetManyByOrganizationIdAsync(orgId);
var readAllAuthorized = (await _authorizationService.AuthorizeAsync(User, orgCollections, CollectionOperations.ReadAll)).Succeeded;
if (!readAllAuthorized)
{
var collections = await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId.Value);
orgCollections = collections.Where(c => c.OrganizationId == orgId);
}
}
else
{
orgCollections = await _collectionService.GetOrganizationCollectionsAsync(orgId);
}
var responses = orgCollections.Select(c => new CollectionResponseModel(c)); var responses = orgCollections.Select(c => new CollectionResponseModel(c));
return new ListResponseModel<CollectionResponseModel>(responses); return new ListResponseModel<CollectionResponseModel>(responses);

View File

@ -20,6 +20,8 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
private readonly ICollectionRepository _collectionRepository; private readonly ICollectionRepository _collectionRepository;
private readonly IFeatureService _featureService; private readonly IFeatureService _featureService;
private bool FlexibleCollectionsIsEnabled => _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext);
public CollectionAuthorizationHandler(ICurrentContext currentContext, ICollectionRepository collectionRepository, public CollectionAuthorizationHandler(ICurrentContext currentContext, ICollectionRepository collectionRepository,
IFeatureService featureService) IFeatureService featureService)
{ {
@ -31,14 +33,14 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
CollectionOperationRequirement requirement, ICollection<Collection> resources) CollectionOperationRequirement requirement, ICollection<Collection> resources)
{ {
if (!_featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext)) if (!FlexibleCollectionsIsEnabled)
{ {
// Flexible collections is OFF, should not be using this handler // Flexible collections is OFF, should not be using this handler
throw new FeatureUnavailableException("Flexible collections is OFF when it should be ON."); throw new FeatureUnavailableException("Flexible collections is OFF when it should be ON.");
} }
// Establish pattern of authorization handler null checking passed resources // Establish pattern of authorization handler null checking passed resources
if (resources == null || !resources.Any()) if (resources == null)
{ {
context.Fail(); context.Fail();
return; return;
@ -72,6 +74,10 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
await CanCreateAsync(context, requirement, org); await CanCreateAsync(context, requirement, org);
break; break;
case not null when requirement.Name == nameof(CollectionOperations.ReadAll):
await CanReadAllAsync(context, requirement, org);
break;
case not null when requirement == CollectionOperations.Delete: case not null when requirement == CollectionOperations.Delete:
await CanDeleteAsync(context, requirement, resources, org); await CanDeleteAsync(context, requirement, resources, org);
break; break;
@ -79,6 +85,10 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
case not null when requirement == CollectionOperations.ModifyAccess: case not null when requirement == CollectionOperations.ModifyAccess:
await CanManageCollectionAccessAsync(context, requirement, resources, org); await CanManageCollectionAccessAsync(context, requirement, resources, org);
break; break;
default:
context.Fail();
break;
} }
} }
@ -105,6 +115,23 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
context.Fail(); context.Fail();
} }
private async Task CanReadAllAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
CurrentContextOrganization org)
{
if (org.Type is OrganizationUserType.Owner or OrganizationUserType.Admin ||
org.Permissions.ManageGroups ||
org.Permissions.ManageUsers ||
org.Permissions.EditAnyCollection ||
org.Permissions.DeleteAnyCollection ||
org.Permissions.AccessImportExport)
{
context.Succeed(requirement);
return;
}
context.Fail();
}
private async Task CanDeleteAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement, private async Task CanDeleteAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
ICollection<Collection> resources, CurrentContextOrganization org) ICollection<Collection> resources, CurrentContextOrganization org)
{ {

View File

@ -7,6 +7,8 @@ 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 Create = new() { Name = nameof(Create) };
public static readonly CollectionOperationRequirement ReadAll = new() { Name = nameof(ReadAll) };
public static readonly CollectionOperationRequirement Update = new() { Name = nameof(Update) };
public static readonly CollectionOperationRequirement Delete = new() { Name = nameof(Delete) }; public static readonly CollectionOperationRequirement Delete = new() { Name = nameof(Delete) };
/// <summary> /// <summary>
/// The operation that represents creating, updating, or removing collection access. /// The operation that represents creating, updating, or removing collection access.

View File

@ -7,5 +7,6 @@ public interface ICollectionService
{ {
Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection> groups = null, IEnumerable<CollectionAccessSelection> users = null); Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection> groups = null, IEnumerable<CollectionAccessSelection> users = null);
Task DeleteUserAsync(Collection collection, Guid organizationUserId); Task DeleteUserAsync(Collection collection, Guid organizationUserId);
[Obsolete("Pre-Flexible Collections logic.")]
Task<IEnumerable<Collection>> GetOrganizationCollectionsAsync(Guid organizationId); Task<IEnumerable<Collection>> GetOrganizationCollectionsAsync(Guid organizationId);
} }