mirror of
https://github.com/bitwarden/server.git
synced 2025-07-03 17:12:49 -05:00
[AC-1139] Updated CollectionsController to use CollectionAuthorizationHandler in all endpoints if flag is enabled
This commit is contained in:
@ -51,19 +51,46 @@ public class CollectionsController : Controller
|
|||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
public async Task<CollectionResponseModel> Get(Guid orgId, Guid id)
|
public async Task<CollectionResponseModel> Get(Guid orgId, Guid id)
|
||||||
|
{
|
||||||
|
Collection collection;
|
||||||
|
|
||||||
|
if (FlexibleCollectionsIsEnabled)
|
||||||
|
{
|
||||||
|
collection = await _collectionRepository.GetByIdAsync(id, _currentContext.UserId.Value);
|
||||||
|
var readAuthorization = await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Read);
|
||||||
|
if (!readAuthorization.Succeeded)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (!await CanViewCollectionAsync(orgId, id))
|
if (!await CanViewCollectionAsync(orgId, id))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var collection = await GetCollectionAsync(id, orgId);
|
collection = await GetCollectionAsync(id, orgId);
|
||||||
|
}
|
||||||
|
|
||||||
return new CollectionResponseModel(collection);
|
return new CollectionResponseModel(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}/details")]
|
[HttpGet("{id}/details")]
|
||||||
public async Task<CollectionAccessDetailsResponseModel> GetDetails(Guid orgId, Guid id)
|
public async Task<CollectionAccessDetailsResponseModel> GetDetails(Guid orgId, Guid id)
|
||||||
{
|
{
|
||||||
|
if (FlexibleCollectionsIsEnabled)
|
||||||
|
{
|
||||||
|
var (collection, access) = await _collectionRepository.GetByIdWithAccessAsync(id);
|
||||||
|
var readAuthorization = await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Read);
|
||||||
|
if (!readAuthorization.Succeeded)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CollectionAccessDetailsResponseModel(collection, access.Groups, access.Users);
|
||||||
|
}
|
||||||
|
|
||||||
if (!await ViewAtLeastOneCollectionAsync(orgId) && !await _currentContext.ManageUsers(orgId))
|
if (!await ViewAtLeastOneCollectionAsync(orgId) && !await _currentContext.ManageUsers(orgId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -95,9 +122,29 @@ 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 (!FlexibleCollectionsIsEnabled &&
|
if (FlexibleCollectionsIsEnabled)
|
||||||
!await ViewAtLeastOneCollectionAsync(orgId) &&
|
{
|
||||||
!await _currentContext.ManageUsers(orgId) &&
|
var readAllAuthorization = await _authorizationService.AuthorizeAsync(User, null, CollectionOperations.ReadAll(orgId));
|
||||||
|
if (readAllAuthorization.Succeeded)
|
||||||
|
{
|
||||||
|
var collections = await _collectionRepository.GetManyByOrganizationIdWithAccessAsync(orgId);
|
||||||
|
return new ListResponseModel<CollectionAccessDetailsResponseModel>(collections.Select(c =>
|
||||||
|
new CollectionAccessDetailsResponseModel(c.Item1, c.Item2.Groups, c.Item2.Users)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var collections = await _collectionRepository.GetManyByUserIdWithAccessAsync(_currentContext.UserId.Value, orgId);
|
||||||
|
var readAuthorization = await _authorizationService.AuthorizeAsync(User, collections.Select(c => c.Item1), CollectionOperations.Read);
|
||||||
|
if (!readAuthorization.Succeeded)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
return new ListResponseModel<CollectionAccessDetailsResponseModel>(collections.Select(c =>
|
||||||
|
new CollectionAccessDetailsResponseModel(c.Item1, c.Item2.Groups, c.Item2.Users)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await ViewAtLeastOneCollectionAsync(orgId) && !await _currentContext.ManageUsers(orgId) &&
|
||||||
!await _currentContext.ManageGroups(orgId))
|
!await _currentContext.ManageGroups(orgId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -145,6 +192,11 @@ public class CollectionsController : Controller
|
|||||||
{
|
{
|
||||||
var collections = await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId.Value);
|
var collections = await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId.Value);
|
||||||
orgCollections = collections.Where(c => c.OrganizationId == orgId);
|
orgCollections = collections.Where(c => c.OrganizationId == orgId);
|
||||||
|
var authorized = (await _authorizationService.AuthorizeAsync(User, orgCollections, CollectionOperations.Read)).Succeeded;
|
||||||
|
if (!authorized)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -168,7 +220,22 @@ public class CollectionsController : Controller
|
|||||||
[HttpGet("{id}/users")]
|
[HttpGet("{id}/users")]
|
||||||
public async Task<IEnumerable<SelectionReadOnlyResponseModel>> GetUsers(Guid orgId, Guid id)
|
public async Task<IEnumerable<SelectionReadOnlyResponseModel>> GetUsers(Guid orgId, Guid id)
|
||||||
{
|
{
|
||||||
var collection = await GetCollectionAsync(id, orgId);
|
Collection collection;
|
||||||
|
|
||||||
|
if (FlexibleCollectionsIsEnabled)
|
||||||
|
{
|
||||||
|
collection = await _collectionRepository.GetByIdAsync(id);
|
||||||
|
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Read)).Succeeded;
|
||||||
|
if (!authorized)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
collection = await GetCollectionAsync(id, orgId);
|
||||||
|
}
|
||||||
|
|
||||||
var collectionUsers = await _collectionRepository.GetManyUsersByIdAsync(collection.Id);
|
var collectionUsers = await _collectionRepository.GetManyUsersByIdAsync(collection.Id);
|
||||||
var responses = collectionUsers.Select(cu => new SelectionReadOnlyResponseModel(cu));
|
var responses = collectionUsers.Select(cu => new SelectionReadOnlyResponseModel(cu));
|
||||||
return responses;
|
return responses;
|
||||||
@ -197,13 +264,28 @@ public class CollectionsController : Controller
|
|||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
[HttpPost("{id}")]
|
[HttpPost("{id}")]
|
||||||
public async Task<CollectionResponseModel> Put(Guid orgId, Guid id, [FromBody] CollectionRequestModel model)
|
public async Task<CollectionResponseModel> Put(Guid orgId, Guid id, [FromBody] CollectionRequestModel model)
|
||||||
|
{
|
||||||
|
Collection collection;
|
||||||
|
|
||||||
|
if (FlexibleCollectionsIsEnabled)
|
||||||
|
{
|
||||||
|
collection = await _collectionRepository.GetByIdAsync(id);
|
||||||
|
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Update)).Succeeded;
|
||||||
|
if (!authorized)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (!await CanEditCollectionAsync(orgId, id))
|
if (!await CanEditCollectionAsync(orgId, id))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var collection = await GetCollectionAsync(id, orgId);
|
collection = await GetCollectionAsync(id, orgId);
|
||||||
|
}
|
||||||
|
|
||||||
var groups = model.Groups?.Select(g => g.ToSelectionReadOnly());
|
var groups = model.Groups?.Select(g => g.ToSelectionReadOnly());
|
||||||
var users = model.Users?.Select(g => g.ToSelectionReadOnly());
|
var users = model.Users?.Select(g => g.ToSelectionReadOnly());
|
||||||
await _collectionService.SaveAsync(model.ToCollection(collection), groups, users);
|
await _collectionService.SaveAsync(model.ToCollection(collection), groups, users);
|
||||||
@ -212,13 +294,28 @@ public class CollectionsController : Controller
|
|||||||
|
|
||||||
[HttpPut("{id}/users")]
|
[HttpPut("{id}/users")]
|
||||||
public async Task PutUsers(Guid orgId, Guid id, [FromBody] IEnumerable<SelectionReadOnlyRequestModel> model)
|
public async Task PutUsers(Guid orgId, Guid id, [FromBody] IEnumerable<SelectionReadOnlyRequestModel> model)
|
||||||
|
{
|
||||||
|
Collection collection;
|
||||||
|
|
||||||
|
if (FlexibleCollectionsIsEnabled)
|
||||||
|
{
|
||||||
|
collection = await _collectionRepository.GetByIdAsync(id);
|
||||||
|
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.ModifyAccess)).Succeeded;
|
||||||
|
if (!authorized)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (!await CanEditCollectionAsync(orgId, id))
|
if (!await CanEditCollectionAsync(orgId, id))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var collection = await GetCollectionAsync(id, orgId);
|
collection = await GetCollectionAsync(id, orgId);
|
||||||
|
}
|
||||||
|
|
||||||
await _collectionRepository.UpdateUsersAsync(collection.Id, model?.Select(g => g.ToSelectionReadOnly()));
|
await _collectionRepository.UpdateUsersAsync(collection.Id, model?.Select(g => g.ToSelectionReadOnly()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,15 +350,26 @@ public class CollectionsController : Controller
|
|||||||
[HttpPost("{id}/delete")]
|
[HttpPost("{id}/delete")]
|
||||||
public async Task Delete(Guid orgId, Guid id)
|
public async Task Delete(Guid orgId, Guid id)
|
||||||
{
|
{
|
||||||
var collection = await GetCollectionAsync(id, orgId);
|
Collection collection;
|
||||||
|
|
||||||
var authorized = FlexibleCollectionsIsEnabled
|
if (FlexibleCollectionsIsEnabled)
|
||||||
? (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Delete)).Succeeded
|
{
|
||||||
: await CanDeleteCollectionAsync(orgId, id);
|
collection = await _collectionRepository.GetByIdAsync(id);
|
||||||
|
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Delete)).Succeeded;
|
||||||
if (!authorized)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!await CanDeleteCollectionAsync(orgId, id))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
collection = await GetCollectionAsync(id, orgId);
|
||||||
|
}
|
||||||
|
|
||||||
await _deleteCollectionCommand.DeleteAsync(collection);
|
await _deleteCollectionCommand.DeleteAsync(collection);
|
||||||
}
|
}
|
||||||
@ -304,14 +412,45 @@ public class CollectionsController : Controller
|
|||||||
|
|
||||||
[HttpDelete("{id}/user/{orgUserId}")]
|
[HttpDelete("{id}/user/{orgUserId}")]
|
||||||
[HttpPost("{id}/delete-user/{orgUserId}")]
|
[HttpPost("{id}/delete-user/{orgUserId}")]
|
||||||
public async Task Delete(string orgId, string id, string orgUserId)
|
public async Task Delete(Guid orgId, Guid id, Guid orgUserId)
|
||||||
{
|
{
|
||||||
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
|
Collection collection;
|
||||||
await _collectionService.DeleteUserAsync(collection, new Guid(orgUserId));
|
|
||||||
|
if (FlexibleCollectionsIsEnabled)
|
||||||
|
{
|
||||||
|
collection = await _collectionRepository.GetByIdAsync(id);
|
||||||
|
var authorized = (await _authorizationService.AuthorizeAsync(User, collection, CollectionOperations.Delete)).Succeeded;
|
||||||
|
if (!authorized)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!await CanDeleteCollectionAsync(orgId, id))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collection = await GetCollectionAsync(id, orgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _collectionService.DeleteUserAsync(collection, orgUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeprecatedPermissionsGuard()
|
||||||
|
{
|
||||||
|
if (FlexibleCollectionsIsEnabled)
|
||||||
|
{
|
||||||
|
throw new FeatureUnavailableException("Flexible Collections is ON when it should be OFF.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Pre-Flexible Collections logic. Will be replaced by CollectionsAuthorizationHandler.")]
|
||||||
private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
|
private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
|
||||||
{
|
{
|
||||||
|
DeprecatedPermissionsGuard();
|
||||||
|
|
||||||
Collection collection = default;
|
Collection collection = default;
|
||||||
if (await _currentContext.ViewAllCollections(orgId))
|
if (await _currentContext.ViewAllCollections(orgId))
|
||||||
{
|
{
|
||||||
@ -330,14 +469,6 @@ public class CollectionsController : Controller
|
|||||||
return collection;
|
return collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeprecatedPermissionsGuard()
|
|
||||||
{
|
|
||||||
if (FlexibleCollectionsIsEnabled)
|
|
||||||
{
|
|
||||||
throw new FeatureUnavailableException("Flexible Collections is ON when it should be OFF.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("Pre-Flexible Collections logic. Will be replaced by CollectionsAuthorizationHandler.")]
|
[Obsolete("Pre-Flexible Collections logic. Will be replaced by CollectionsAuthorizationHandler.")]
|
||||||
private async Task<bool> CanCreateCollection(Guid orgId, Guid collectionId)
|
private async Task<bool> CanCreateCollection(Guid orgId, Guid collectionId)
|
||||||
{
|
{
|
||||||
@ -352,8 +483,11 @@ public class CollectionsController : Controller
|
|||||||
(o.Permissions?.CreateNewCollections ?? false)) ?? false);
|
(o.Permissions?.CreateNewCollections ?? false)) ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Pre-Flexible Collections logic. Will be replaced by CollectionsAuthorizationHandler.")]
|
||||||
private async Task<bool> CanEditCollectionAsync(Guid orgId, Guid collectionId)
|
private async Task<bool> CanEditCollectionAsync(Guid orgId, Guid collectionId)
|
||||||
{
|
{
|
||||||
|
DeprecatedPermissionsGuard();
|
||||||
|
|
||||||
if (collectionId == default)
|
if (collectionId == default)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -409,8 +543,11 @@ public class CollectionsController : Controller
|
|||||||
&& (o.Permissions?.DeleteAnyCollection ?? false)) ?? false);
|
&& (o.Permissions?.DeleteAnyCollection ?? false)) ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Pre-Flexible Collections logic. Will be replaced by CollectionsAuthorizationHandler.")]
|
||||||
private async Task<bool> CanViewCollectionAsync(Guid orgId, Guid collectionId)
|
private async Task<bool> CanViewCollectionAsync(Guid orgId, Guid collectionId)
|
||||||
{
|
{
|
||||||
|
DeprecatedPermissionsGuard();
|
||||||
|
|
||||||
if (collectionId == default)
|
if (collectionId == default)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -431,8 +568,11 @@ public class CollectionsController : Controller
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Pre-Flexible Collections logic. Will be replaced by CollectionsAuthorizationHandler.")]
|
||||||
private async Task<bool> ViewAtLeastOneCollectionAsync(Guid orgId)
|
private async Task<bool> ViewAtLeastOneCollectionAsync(Guid orgId)
|
||||||
{
|
{
|
||||||
|
DeprecatedPermissionsGuard();
|
||||||
|
|
||||||
return await _currentContext.ViewAllCollections(orgId) || await _currentContext.ViewAssignedCollections(orgId);
|
return await _currentContext.ViewAllCollections(orgId) || await _currentContext.ViewAssignedCollections(orgId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,8 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetOrganizationId = resources.First().OrganizationId;
|
var targetOrganizationId = requirement.OrganizationId != default ?
|
||||||
|
requirement.OrganizationId : resources.First().OrganizationId;
|
||||||
|
|
||||||
// Ensure all target collections belong to the same organization
|
// Ensure all target collections belong to the same organization
|
||||||
if (resources.Any(tc => tc.OrganizationId != targetOrganizationId))
|
if (resources.Any(tc => tc.OrganizationId != targetOrganizationId))
|
||||||
@ -71,7 +72,11 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
|
|||||||
switch (requirement)
|
switch (requirement)
|
||||||
{
|
{
|
||||||
case not null when requirement == CollectionOperations.Create:
|
case not null when requirement == CollectionOperations.Create:
|
||||||
await CanCreateAsync(context, requirement, org);
|
await CanCreateAsync(context, requirement, resources, org);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case not null when requirement == CollectionOperations.Read:
|
||||||
|
await CanReadAsync(context, requirement, resources, org);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case not null when requirement.Name == nameof(CollectionOperations.ReadAll):
|
case not null when requirement.Name == nameof(CollectionOperations.ReadAll):
|
||||||
@ -82,6 +87,7 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
|
|||||||
await CanDeleteAsync(context, requirement, resources, org);
|
await CanDeleteAsync(context, requirement, resources, org);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case not null when requirement == CollectionOperations.Update:
|
||||||
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;
|
||||||
@ -93,8 +99,14 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task CanCreateAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
|
private async Task CanCreateAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
|
||||||
CurrentContextOrganization org)
|
ICollection<Collection> targetCollections, CurrentContextOrganization org)
|
||||||
{
|
{
|
||||||
|
if (targetCollections.Any(c => c.Id != default))
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If false, all organization members are allowed to create collections
|
// If false, all organization members are allowed to create collections
|
||||||
if (!org.LimitCollectionCreationDeletion)
|
if (!org.LimitCollectionCreationDeletion)
|
||||||
{
|
{
|
||||||
@ -115,26 +127,63 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
|
|||||||
context.Fail();
|
context.Fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CanReadAllAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
|
private async Task CanReadAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
|
||||||
CurrentContextOrganization org)
|
ICollection<Collection> targetCollections, CurrentContextOrganization org)
|
||||||
{
|
{
|
||||||
|
if (targetCollections.Any(c => c.Id == default))
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (org.Type is OrganizationUserType.Owner or OrganizationUserType.Admin ||
|
if (org.Type is OrganizationUserType.Owner or OrganizationUserType.Admin ||
|
||||||
org.Permissions.ManageGroups ||
|
await _currentContext.ProviderUserForOrgAsync(org.Id))
|
||||||
org.Permissions.ManageUsers ||
|
|
||||||
org.Permissions.EditAnyCollection ||
|
|
||||||
org.Permissions.DeleteAnyCollection ||
|
|
||||||
org.Permissions.AccessImportExport)
|
|
||||||
{
|
{
|
||||||
context.Succeed(requirement);
|
context.Succeed(requirement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var manageableCollectionIds =
|
||||||
|
(await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId!.Value))
|
||||||
|
.Where(c => c.OrganizationId == org.Id)
|
||||||
|
.Select(c => c.Id)
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
// The acting user does not have permission to manage all target collections, fail
|
||||||
|
if (targetCollections.Any(c => !manageableCollectionIds.Contains(c.Id)))
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CanReadAllAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
|
||||||
|
CurrentContextOrganization org)
|
||||||
|
{
|
||||||
|
if (org.Type is not (OrganizationUserType.Owner or OrganizationUserType.Admin) &&
|
||||||
|
!org.Permissions.ManageGroups &&
|
||||||
|
!org.Permissions.ManageUsers &&
|
||||||
|
!org.Permissions.EditAnyCollection &&
|
||||||
|
!org.Permissions.DeleteAnyCollection &&
|
||||||
|
!org.Permissions.AccessImportExport)
|
||||||
|
{
|
||||||
context.Fail();
|
context.Fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task CanDeleteAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
|
private async Task CanDeleteAsync(AuthorizationHandlerContext context, CollectionOperationRequirement requirement,
|
||||||
ICollection<Collection> resources, CurrentContextOrganization org)
|
ICollection<Collection> targetCollections, CurrentContextOrganization org)
|
||||||
{
|
{
|
||||||
|
if (targetCollections.Any(c => c.Id == default))
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Owners, Admins, Providers, and users with DeleteAnyCollection permission can always delete collections
|
// Owners, Admins, Providers, and users with DeleteAnyCollection permission can always delete collections
|
||||||
if (
|
if (
|
||||||
org.Type is OrganizationUserType.Owner or OrganizationUserType.Admin ||
|
org.Type is OrganizationUserType.Owner or OrganizationUserType.Admin ||
|
||||||
@ -160,7 +209,7 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
|
|||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
|
|
||||||
// The acting user does not have permission to manage all target collections, fail
|
// The acting user does not have permission to manage all target collections, fail
|
||||||
if (resources.Any(c => !manageableCollectionIds.Contains(c.Id)))
|
if (targetCollections.Any(c => !manageableCollectionIds.Contains(c.Id)))
|
||||||
{
|
{
|
||||||
context.Fail();
|
context.Fail();
|
||||||
return;
|
return;
|
||||||
@ -175,6 +224,12 @@ public class CollectionAuthorizationHandler : BulkAuthorizationHandler<Collectio
|
|||||||
private async Task CanManageCollectionAccessAsync(AuthorizationHandlerContext context,
|
private async Task CanManageCollectionAccessAsync(AuthorizationHandlerContext context,
|
||||||
IAuthorizationRequirement requirement, ICollection<Collection> targetCollections, CurrentContextOrganization org)
|
IAuthorizationRequirement requirement, ICollection<Collection> targetCollections, CurrentContextOrganization org)
|
||||||
{
|
{
|
||||||
|
if (targetCollections.Any(c => c.Id == default))
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Owners, Admins, Providers, and users with EditAnyCollection permission can always manage collection access
|
// Owners, Admins, Providers, and users with EditAnyCollection permission can always manage collection access
|
||||||
if (
|
if (
|
||||||
org.Permissions is { EditAnyCollection: true } ||
|
org.Permissions is { EditAnyCollection: true } ||
|
||||||
|
@ -18,10 +18,12 @@ 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 Read = new() { Name = nameof(Read) };
|
||||||
public static CollectionOperationRequirement ReadAll(Guid organizationId)
|
public static CollectionOperationRequirement ReadAll(Guid organizationId)
|
||||||
{
|
{
|
||||||
return new CollectionOperationRequirement(nameof(ReadAll), organizationId);
|
return new CollectionOperationRequirement(nameof(ReadAll), organizationId);
|
||||||
}
|
}
|
||||||
|
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.
|
||||||
|
Reference in New Issue
Block a user