1
0
mirror of https://github.com/bitwarden/server synced 2025-12-25 12:43:14 +00:00

[AC-1174] Bulk Collection Management (#3229)

* [AC-1174] Update SelectionReadOnlyRequestModel to use Guid for Id property

* [AC-1174] Introduce initial bulk-access collection endpoint

* [AC-1174] Introduce BulkAddCollectionAccessCommand and validation logic/tests

* [AC-1174] Add CreateOrUpdateAccessMany method to CollectionRepository

* [AC-1174] Add event logs for bulk add collection access command

* [AC-1174] Add User_BumpAccountRevisionDateByCollectionIds and database migration script

* [AC-1174] Implement EF repository method

* [AC-1174] Improve null checks

* [AC-1174] Remove unnecessary BulkCollectionAccessRequestModel helpers

* [AC-1174] Add unit tests for new controller endpoint

* [AC-1174] Fix formatting

* [AC-1174] Remove comment

* [AC-1174] Remove redundant organizationId parameter

* [AC-1174] Ensure user and group Ids are distinct

* [AC-1174] Cleanup tests based on PR feedback

* [AC-1174] Formatting

* [AC-1174] Update CollectionGroup alias in the sproc

* [AC-1174] Add some additional comments to SQL sproc

* [AC-1174] Add comment explaining additional SaveChangesAsync call

---------

Co-authored-by: Thomas Rittson <trittson@bitwarden.com>
This commit is contained in:
Shane Melton
2023-09-26 09:30:07 -07:00
committed by GitHub
parent 2c7d02dcbb
commit 5d431adbd4
16 changed files with 943 additions and 5 deletions

View File

@@ -0,0 +1,95 @@
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Bit.Core.OrganizationFeatures.OrganizationCollections.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
namespace Bit.Core.OrganizationFeatures.OrganizationCollections;
public class BulkAddCollectionAccessCommand : IBulkAddCollectionAccessCommand
{
private readonly ICollectionRepository _collectionRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IGroupRepository _groupRepository;
private readonly IEventService _eventService;
public BulkAddCollectionAccessCommand(
ICollectionRepository collectionRepository,
IOrganizationUserRepository organizationUserRepository,
IGroupRepository groupRepository,
IEventService eventService)
{
_collectionRepository = collectionRepository;
_organizationUserRepository = organizationUserRepository;
_groupRepository = groupRepository;
_eventService = eventService;
}
public async Task AddAccessAsync(ICollection<Collection> collections,
ICollection<CollectionAccessSelection> users,
ICollection<CollectionAccessSelection> groups)
{
await ValidateRequestAsync(collections, users, groups);
await _collectionRepository.CreateOrUpdateAccessForManyAsync(
collections.First().OrganizationId,
collections.Select(c => c.Id),
users,
groups
);
await _eventService.LogCollectionEventsAsync(collections.Select(c =>
(c, EventType.Collection_Updated, (DateTime?)DateTime.UtcNow)));
}
private async Task ValidateRequestAsync(ICollection<Collection> collections, ICollection<CollectionAccessSelection> usersAccess, ICollection<CollectionAccessSelection> groupsAccess)
{
if (collections == null || collections.Count == 0)
{
throw new BadRequestException("No collections were provided.");
}
var orgId = collections.First().OrganizationId;
if (collections.Any(c => c.OrganizationId != orgId))
{
throw new BadRequestException("All collections must belong to the same organization.");
}
var collectionUserIds = usersAccess?.Select(u => u.Id).Distinct().ToList();
if (collectionUserIds is { Count: > 0 })
{
var users = await _organizationUserRepository.GetManyAsync(collectionUserIds);
if (users.Count != collectionUserIds.Count)
{
throw new BadRequestException("One or more users do not exist.");
}
if (users.Any(u => u.OrganizationId != orgId))
{
throw new BadRequestException("One or more users do not belong to the same organization as the collection being assigned.");
}
}
var collectionGroupIds = groupsAccess?.Select(g => g.Id).Distinct().ToList();
if (collectionGroupIds is { Count: > 0 })
{
var groups = await _groupRepository.GetManyByManyIds(collectionGroupIds);
if (groups.Count != collectionGroupIds.Count)
{
throw new BadRequestException("One or more groups do not exist.");
}
if (groups.Any(g => g.OrganizationId != orgId))
{
throw new BadRequestException("One or more groups do not belong to the same organization as the collection being assigned.");
}
}
}
}