mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 16:42:50 -05:00
[AC-1139] Lining up collection access data with Manage = true if feature flag is off
This commit is contained in:
@ -5,6 +5,7 @@ using Bit.Core;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationCollections.Interfaces;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -19,6 +20,7 @@ namespace Bit.Api.Controllers;
|
||||
public class CollectionsController : Controller
|
||||
{
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly ICollectionService _collectionService;
|
||||
private readonly IDeleteCollectionCommand _deleteCollectionCommand;
|
||||
private readonly IUserService _userService;
|
||||
@ -29,6 +31,7 @@ public class CollectionsController : Controller
|
||||
|
||||
public CollectionsController(
|
||||
ICollectionRepository collectionRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
ICollectionService collectionService,
|
||||
IDeleteCollectionCommand deleteCollectionCommand,
|
||||
IUserService userService,
|
||||
@ -38,6 +41,7 @@ public class CollectionsController : Controller
|
||||
IFeatureService featureService)
|
||||
{
|
||||
_collectionRepository = collectionRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_collectionService = collectionService;
|
||||
_deleteCollectionCommand = deleteCollectionCommand;
|
||||
_userService = userService;
|
||||
@ -296,6 +300,8 @@ public class CollectionsController : Controller
|
||||
public async Task PutUsers(Guid orgId, Guid id, [FromBody] IEnumerable<SelectionReadOnlyRequestModel> model)
|
||||
{
|
||||
Collection collection;
|
||||
var users = model?.Select(g => g.ToSelectionReadOnly()).ToList() ??
|
||||
new List<CollectionAccessSelection>();
|
||||
|
||||
if (FlexibleCollectionsIsEnabled)
|
||||
{
|
||||
@ -314,9 +320,26 @@ public class CollectionsController : Controller
|
||||
}
|
||||
|
||||
collection = await GetCollectionAsync(id, orgId);
|
||||
|
||||
// If not using Flexible Collections
|
||||
// all users with EditAnyCollection permission should have Can Manage permission for the collection
|
||||
var organizationUsers = await _organizationUserRepository
|
||||
.GetManyByOrganizationAsync(collection.OrganizationId, null);
|
||||
foreach (var orgUser in organizationUsers.Where(ou => ou.GetPermissions()?.EditAnyCollection ?? false))
|
||||
{
|
||||
var user = users.FirstOrDefault(u => u.Id == orgUser.Id);
|
||||
if (user != null)
|
||||
{
|
||||
user.Manage = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
users.Add(new CollectionAccessSelection { Id = orgUser.Id, Manage = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await _collectionRepository.UpdateUsersAsync(collection.Id, model?.Select(g => g.ToSelectionReadOnly()));
|
||||
await _collectionRepository.UpdateUsersAsync(collection.Id, users);
|
||||
}
|
||||
|
||||
[HttpPost("bulk-access")]
|
||||
|
@ -53,21 +53,40 @@ public class CollectionService : ICollectionService
|
||||
}
|
||||
|
||||
var groupsList = groups?.ToList();
|
||||
var usersList = users?.ToList();
|
||||
var usersList = users?.ToList() ?? new List<CollectionAccessSelection>();
|
||||
|
||||
// If using Flexible Collections - a collection should always have someone with Can Manage permissions
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext))
|
||||
{
|
||||
var groupHasManageAccess = groupsList?.Any(g => g.Manage) ?? false;
|
||||
var userHasManageAccess = usersList?.Any(u => u.Manage) ?? false;
|
||||
var userHasManageAccess = usersList.Any(u => u.Manage);
|
||||
if (!groupHasManageAccess && !userHasManageAccess)
|
||||
{
|
||||
throw new BadRequestException(
|
||||
"At least one member or group must have can manage permission.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not using Flexible Collections
|
||||
// all users with EditAnyCollection permission should have Can Manage permission for the collection
|
||||
var organizationUsers = await _organizationUserRepository
|
||||
.GetManyByOrganizationAsync(collection.OrganizationId, null);
|
||||
foreach (var orgUser in organizationUsers.Where(ou => ou.GetPermissions()?.EditAnyCollection ?? false))
|
||||
{
|
||||
var user = usersList.FirstOrDefault(u => u.Id == orgUser.Id);
|
||||
if (user != null)
|
||||
{
|
||||
user.Manage = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
usersList.Add(new CollectionAccessSelection { Id = orgUser.Id, Manage = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (collection.Id == default(Guid))
|
||||
if (collection.Id == default)
|
||||
{
|
||||
if (org.MaxCollections.HasValue)
|
||||
{
|
||||
|
@ -54,6 +54,9 @@ public class OrganizationService : IOrganizationService
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery;
|
||||
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
|
||||
private readonly IFeatureService _featureService;
|
||||
|
||||
private bool FlexibleCollectionsIsEnabled => _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext);
|
||||
|
||||
public OrganizationService(
|
||||
IOrganizationRepository organizationRepository,
|
||||
@ -82,7 +85,8 @@ public class OrganizationService : IOrganizationService
|
||||
IProviderOrganizationRepository providerOrganizationRepository,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery,
|
||||
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand)
|
||||
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
||||
IFeatureService featureService)
|
||||
{
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
@ -111,6 +115,7 @@ public class OrganizationService : IOrganizationService
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery;
|
||||
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
||||
_featureService = featureService;
|
||||
}
|
||||
|
||||
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
|
||||
@ -876,7 +881,6 @@ public class OrganizationService : IOrganizationService
|
||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||
}
|
||||
|
||||
var orgUsers = new List<OrganizationUser>();
|
||||
var limitedCollectionOrgUsers = new List<(OrganizationUser, IEnumerable<CollectionAccessSelection>)>();
|
||||
var orgUserGroups = new List<(OrganizationUser, IEnumerable<Guid>)>();
|
||||
var orgUserInvitedCount = 0;
|
||||
@ -915,14 +919,22 @@ public class OrganizationService : IOrganizationService
|
||||
orgUser.Permissions = JsonSerializer.Serialize(invite.Permissions, JsonHelpers.CamelCase);
|
||||
}
|
||||
|
||||
if (!orgUser.AccessAll && invite.Collections.Any())
|
||||
var collections = invite.Collections;
|
||||
if (!FlexibleCollectionsIsEnabled)
|
||||
{
|
||||
limitedCollectionOrgUsers.Add((orgUser, invite.Collections));
|
||||
}
|
||||
else
|
||||
// If not using Flexible Collections - add access to all collections if user has EditAnyCollection or AccessAll permissions
|
||||
if (orgUser.GetPermissions()?.EditAnyCollection ?? false)
|
||||
{
|
||||
orgUsers.Add(orgUser);
|
||||
var orgCollections = await _collectionRepository.GetManyByOrganizationIdAsync(orgUser.OrganizationId);
|
||||
collections = orgCollections.Select(c => new CollectionAccessSelection { Id = c.Id, Manage = true });
|
||||
}
|
||||
else if (orgUser.AccessAll)
|
||||
{
|
||||
var orgCollections = await _collectionRepository.GetManyByOrganizationIdAsync(orgUser.OrganizationId);
|
||||
collections = orgCollections.Select(c => new CollectionAccessSelection { Id = c.Id, ReadOnly = true });
|
||||
}
|
||||
}
|
||||
limitedCollectionOrgUsers.Add((orgUser, collections));
|
||||
|
||||
if (invite.Groups != null && invite.Groups.Any())
|
||||
{
|
||||
@ -947,7 +959,6 @@ public class OrganizationService : IOrganizationService
|
||||
var prorationDate = DateTime.UtcNow;
|
||||
try
|
||||
{
|
||||
await _organizationUserRepository.CreateManyAsync(orgUsers);
|
||||
foreach (var (orgUser, collections) in limitedCollectionOrgUsers)
|
||||
{
|
||||
await _organizationUserRepository.CreateAsync(orgUser, collections);
|
||||
@ -969,7 +980,7 @@ public class OrganizationService : IOrganizationService
|
||||
await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(smSubscriptionUpdate);
|
||||
}
|
||||
await AutoAddSeatsAsync(organization, newSeatsRequired, prorationDate);
|
||||
await SendInvitesAsync(orgUsers.Concat(limitedCollectionOrgUsers.Select(u => u.Item1)), organization);
|
||||
await SendInvitesAsync(limitedCollectionOrgUsers.Select(u => u.Item1), organization);
|
||||
|
||||
await _referenceEventService.RaiseEventAsync(
|
||||
new ReferenceEvent(ReferenceEventType.InvitedUsers, organization, _currentContext)
|
||||
@ -980,7 +991,7 @@ public class OrganizationService : IOrganizationService
|
||||
catch (Exception e)
|
||||
{
|
||||
// Revert any added users.
|
||||
var invitedOrgUserIds = orgUsers.Select(u => u.Id).Concat(limitedCollectionOrgUsers.Select(u => u.Item1.Id));
|
||||
var invitedOrgUserIds = limitedCollectionOrgUsers.Select(u => u.Item1.Id);
|
||||
await _organizationUserRepository.DeleteManyAsync(invitedOrgUserIds);
|
||||
var currentOrganization = await _organizationRepository.GetByIdAsync(organization.Id);
|
||||
|
||||
@ -1010,7 +1021,7 @@ public class OrganizationService : IOrganizationService
|
||||
throw new AggregateException("One or more errors occurred while inviting users.", exceptions);
|
||||
}
|
||||
|
||||
return (orgUsers, events);
|
||||
return (limitedCollectionOrgUsers.Select(orgUser => orgUser.Item1).ToList(), events);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Tuple<OrganizationUser, string>>> ResendInvitesAsync(Guid organizationId, Guid? invitingUserId,
|
||||
@ -1436,11 +1447,25 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
}
|
||||
|
||||
if (user.AccessAll)
|
||||
// If not using Flexible Collections - add access to all collections if user has EditAnyCollection or AccessAll permissions
|
||||
if (!FlexibleCollectionsIsEnabled)
|
||||
{
|
||||
// We don't need any collections if we're flagged to have all access.
|
||||
collections = new List<CollectionAccessSelection>();
|
||||
if (user.GetPermissions()?.EditAnyCollection ?? false)
|
||||
{
|
||||
var orgCollections = await _collectionRepository.GetManyByOrganizationIdAsync(user.OrganizationId);
|
||||
collections = orgCollections.Select(c => new CollectionAccessSelection { Id = c.Id, Manage = true });
|
||||
}
|
||||
else if (user.AccessAll)
|
||||
{
|
||||
var orgCollections = await _collectionRepository.GetManyByOrganizationIdAsync(user.OrganizationId);
|
||||
collections = orgCollections.Select(c => new CollectionAccessSelection { Id = c.Id, ReadOnly = true });
|
||||
}
|
||||
else
|
||||
{
|
||||
collections = collections.Where(c => !c.Manage);
|
||||
}
|
||||
}
|
||||
|
||||
await _organizationUserRepository.ReplaceAsync(user, collections);
|
||||
|
||||
if (groups != null)
|
||||
|
Reference in New Issue
Block a user