1
0
mirror of https://github.com/bitwarden/server.git synced 2025-02-02 23:41:21 +01:00

[AC-1880] - Public API - Update collection permission associations with Manage property (#3656)

* Add missing hide-passwords permission to api models

* Update src/Api/Auth/Models/Public/AssociationWithPermissionsBaseModel.cs

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>

* Rename ToSelectionReadOnly to ToCollectionAccessSelection

* Remove Required attribute which would break backwards compatability

* Update src/Api/Auth/Models/Public/Request/AssociationWithPermissionsRequestModel.cs

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>

* feat: add Manage property to collection permissions associations, refs AC-1880

* feat: throw if not allowed to send manage property, refs AC-1880

* fix: format, refs AC-1880

* feat: replace ambiguous call for all organizations in cache with specific orgId, refs AC-1880

* feat: move all property assignements back into CollectionAccessSelection init, refs AC-1880

* feat: align bad request messaging, refs AC-1880

---------

Co-authored-by: Daniel James Smith <djsmith@web.de>
Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com>
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
Vincent Salucci 2024-01-22 10:44:33 -06:00 committed by GitHub
parent e6bb6e1114
commit aeca1722fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 37 additions and 11 deletions

View File

@ -110,8 +110,8 @@ public class GroupsController : Controller
public async Task<IActionResult> Post([FromBody] GroupCreateUpdateRequestModel model)
{
var group = model.ToGroup(_currentContext.OrganizationId.Value);
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection());
var organization = await _organizationRepository.GetByIdAsync(_currentContext.OrganizationId.Value);
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organization.FlexibleCollections));
await _createGroupCommand.CreateGroupAsync(group, organization, associations);
var response = new GroupResponseModel(group, associations);
return new JsonResult(response);
@ -139,8 +139,8 @@ public class GroupsController : Controller
}
var updatedGroup = model.ToGroup(existingGroup);
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection());
var organization = await _organizationRepository.GetByIdAsync(_currentContext.OrganizationId.Value);
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organization.FlexibleCollections));
await _updateGroupCommand.UpdateGroupAsync(updatedGroup, organization, associations);
var response = new GroupResponseModel(updatedGroup, associations);
return new JsonResult(response);

View File

@ -23,6 +23,7 @@ public class MembersController : Controller
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
private readonly IUpdateOrganizationUserGroupsCommand _updateOrganizationUserGroupsCommand;
private readonly IApplicationCacheService _applicationCacheService;
public MembersController(
IOrganizationUserRepository organizationUserRepository,
@ -30,7 +31,8 @@ public class MembersController : Controller
IOrganizationService organizationService,
IUserService userService,
ICurrentContext currentContext,
IUpdateOrganizationUserGroupsCommand updateOrganizationUserGroupsCommand)
IUpdateOrganizationUserGroupsCommand updateOrganizationUserGroupsCommand,
IApplicationCacheService applicationCacheService)
{
_organizationUserRepository = organizationUserRepository;
_groupRepository = groupRepository;
@ -38,6 +40,7 @@ public class MembersController : Controller
_userService = userService;
_currentContext = currentContext;
_updateOrganizationUserGroupsCommand = updateOrganizationUserGroupsCommand;
_applicationCacheService = applicationCacheService;
}
/// <summary>
@ -119,7 +122,8 @@ public class MembersController : Controller
[ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Post([FromBody] MemberCreateRequestModel model)
{
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection());
var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(_currentContext.OrganizationId.Value);
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organizationAbility?.FlexibleCollections ?? false));
var invite = new OrganizationUserInvite
{
Emails = new List<string> { model.Email },
@ -154,7 +158,8 @@ public class MembersController : Controller
return new NotFoundResult();
}
var updatedUser = model.ToOrganizationUser(existingUser);
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection());
var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(_currentContext.OrganizationId.Value);
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organizationAbility?.FlexibleCollections ?? false));
await _organizationService.SaveUserAsync(updatedUser, null, associations, model.Groups);
MemberResponseModel response = null;
if (existingUser.UserId.HasValue)

View File

@ -20,4 +20,9 @@ public abstract class AssociationWithPermissionsBaseModel
/// This prevents easy copy-and-paste of hidden items, however it may not completely prevent user access.
/// </summary>
public bool? HidePasswords { get; set; }
/// <summary>
/// When true, the manage permission allows a user to both edit the ciphers within a collection and edit the users/groups that are assigned to the collection.
/// This field will not affect behavior until the Flexible Collections functionality is released in Q1, 2024.
/// </summary>
public bool? Manage { get; set; }
}

View File

@ -1,16 +1,27 @@
using Bit.Core.Models.Data;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
namespace Bit.Api.AdminConsole.Public.Models.Request;
public class AssociationWithPermissionsRequestModel : AssociationWithPermissionsBaseModel
{
public CollectionAccessSelection ToCollectionAccessSelection()
public CollectionAccessSelection ToCollectionAccessSelection(bool migratedToFlexibleCollections)
{
return new CollectionAccessSelection
var collectionAccessSelection = new CollectionAccessSelection
{
Id = Id.Value,
ReadOnly = ReadOnly.Value,
HidePasswords = HidePasswords.GetValueOrDefault()
HidePasswords = HidePasswords.GetValueOrDefault(),
Manage = Manage.GetValueOrDefault()
};
// Throws if the org has not migrated to use FC but has passed in a Manage value in the request
if (!migratedToFlexibleCollections && Manage.HasValue)
{
throw new BadRequestException(
"Your organization must be using the latest collection enhancements to use the Manage property.");
}
return collectionAccessSelection;
}
}

View File

@ -13,5 +13,6 @@ public class AssociationWithPermissionsResponseModel : AssociationWithPermission
Id = selection.Id;
ReadOnly = selection.ReadOnly;
HidePasswords = selection.HidePasswords;
Manage = selection.Manage;
}
}

View File

@ -16,15 +16,18 @@ public class CollectionsController : Controller
private readonly ICollectionRepository _collectionRepository;
private readonly ICollectionService _collectionService;
private readonly ICurrentContext _currentContext;
private readonly IApplicationCacheService _applicationCacheService;
public CollectionsController(
ICollectionRepository collectionRepository,
ICollectionService collectionService,
ICurrentContext currentContext)
ICurrentContext currentContext,
IApplicationCacheService applicationCacheService)
{
_collectionRepository = collectionRepository;
_collectionService = collectionService;
_currentContext = currentContext;
_applicationCacheService = applicationCacheService;
}
/// <summary>
@ -89,7 +92,8 @@ public class CollectionsController : Controller
return new NotFoundResult();
}
var updatedCollection = model.ToCollection(existingCollection);
var associations = model.Groups?.Select(c => c.ToCollectionAccessSelection());
var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(_currentContext.OrganizationId.Value);
var associations = model.Groups?.Select(c => c.ToCollectionAccessSelection(organizationAbility?.FlexibleCollections ?? false));
await _collectionService.SaveAsync(updatedCollection, associations);
var response = new CollectionResponseModel(updatedCollection, associations);
return new JsonResult(response);