mirror of
https://github.com/bitwarden/server.git
synced 2025-02-01 23:31:41 +01:00
[AC-1880] Public API - Deprecated properties (#3706)
* feat: remove required for AccessAll and add xmldoc for usage restrictions, refs AC-1880 * feat: add validation for create group workflow wrt manage property, refs AC-1880 * feat: add validation for update group workflow wrt manage property, refs AC-1880 * feat: add validation for create and update member workflow wrt manage property, refs AC-1880 * feat: add validation for update collection workflow wrt manage property, refs AC-1880 * fix: flaky Public/GroupsControllerTests + more test coverage, refs AC-1880
This commit is contained in:
parent
7747744ff9
commit
d29755de5a
@ -125,7 +125,7 @@ public class GroupsController : Controller
|
||||
|
||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||
var group = model.ToGroup(orgIdGuid);
|
||||
await _createGroupCommand.CreateGroupAsync(group, organization, model.Collections?.Select(c => c.ToSelectionReadOnly()), model.Users);
|
||||
await _createGroupCommand.CreateGroupAsync(group, organization, model.Collections?.Select(c => c.ToSelectionReadOnly()).ToList(), model.Users);
|
||||
|
||||
return new GroupResponseModel(group);
|
||||
}
|
||||
@ -143,7 +143,7 @@ public class GroupsController : Controller
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||
|
||||
await _updateGroupCommand.UpdateGroupAsync(model.ToGroup(group), organization, model.Collections?.Select(c => c.ToSelectionReadOnly()), model.Users);
|
||||
await _updateGroupCommand.UpdateGroupAsync(model.ToGroup(group), organization, model.Collections?.Select(c => c.ToSelectionReadOnly()).ToList(), model.Users);
|
||||
return new GroupResponseModel(group);
|
||||
}
|
||||
|
||||
|
@ -308,7 +308,7 @@ public class OrganizationUsersController : Controller
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _organizationService.SaveUserAsync(model.ToOrganizationUser(organizationUser), userId.Value,
|
||||
model.Collections?.Select(c => c.ToSelectionReadOnly()), model.Groups);
|
||||
model.Collections?.Select(c => c.ToSelectionReadOnly()).ToList(), model.Groups);
|
||||
}
|
||||
|
||||
[HttpPut("{id}/groups")]
|
||||
|
@ -111,7 +111,7 @@ public class GroupsController : Controller
|
||||
{
|
||||
var group = model.ToGroup(_currentContext.OrganizationId.Value);
|
||||
var organization = await _organizationRepository.GetByIdAsync(_currentContext.OrganizationId.Value);
|
||||
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organization.FlexibleCollections));
|
||||
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organization.FlexibleCollections)).ToList();
|
||||
await _createGroupCommand.CreateGroupAsync(group, organization, associations);
|
||||
var response = new GroupResponseModel(group, associations);
|
||||
return new JsonResult(response);
|
||||
@ -140,7 +140,7 @@ public class GroupsController : Controller
|
||||
|
||||
var updatedGroup = model.ToGroup(existingGroup);
|
||||
var organization = await _organizationRepository.GetByIdAsync(_currentContext.OrganizationId.Value);
|
||||
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organization.FlexibleCollections));
|
||||
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organization.FlexibleCollections)).ToList();
|
||||
await _updateGroupCommand.UpdateGroupAsync(updatedGroup, organization, associations);
|
||||
var response = new GroupResponseModel(updatedGroup, associations);
|
||||
return new JsonResult(response);
|
||||
|
@ -5,7 +5,6 @@ using Bit.Api.Models.Public.Response;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -123,14 +122,7 @@ public class MembersController : Controller
|
||||
public async Task<IActionResult> Post([FromBody] MemberCreateRequestModel model)
|
||||
{
|
||||
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 },
|
||||
Type = model.Type.Value,
|
||||
AccessAll = model.AccessAll.Value,
|
||||
Collections = associations
|
||||
};
|
||||
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organizationAbility?.FlexibleCollections ?? false)).ToList();
|
||||
var user = await _organizationService.InviteUserAsync(_currentContext.OrganizationId.Value, null,
|
||||
model.Email, model.Type.Value, model.AccessAll.Value, model.ExternalId, associations, model.Groups);
|
||||
var response = new MemberResponseModel(user, associations);
|
||||
@ -159,7 +151,7 @@ public class MembersController : Controller
|
||||
}
|
||||
var updatedUser = model.ToOrganizationUser(existingUser);
|
||||
var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(_currentContext.OrganizationId.Value);
|
||||
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organizationAbility?.FlexibleCollections ?? false));
|
||||
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(organizationAbility?.FlexibleCollections ?? false)).ToList();
|
||||
await _organizationService.SaveUserAsync(updatedUser, null, associations, model.Groups);
|
||||
MemberResponseModel response = null;
|
||||
if (existingUser.UserId.HasValue)
|
||||
|
@ -22,7 +22,7 @@ public abstract class AssociationWithPermissionsBaseModel
|
||||
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.
|
||||
/// This field will not affect behavior until your organization is using the latest collection enhancements (Releasing Q1, 2024)
|
||||
/// </summary>
|
||||
public bool? Manage { get; set; }
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ public abstract class GroupBaseModel
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Determines if this group can access all collections within the organization, or only the associated
|
||||
/// collections. If set to <c>true</c>, this option overrides any collection assignments.
|
||||
/// collections. If set to <c>true</c>, this option overrides any collection assignments. If your organization is using
|
||||
/// the latest collection enhancements, you will not be allowed to set this property to <c>true</c>.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? AccessAll { get; set; }
|
||||
/// <summary>
|
||||
/// External identifier for reference or linking this group to another system, such as a user directory.
|
||||
|
@ -36,15 +36,16 @@ public abstract class MemberBaseModel
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The member's type (or role) within the organization.
|
||||
/// The member's type (or role) within the organization. If your organization has is using the latest collection enhancements,
|
||||
/// you will not be allowed to assign the Manager role (OrganizationUserType = 3).
|
||||
/// </summary>
|
||||
[Required]
|
||||
public OrganizationUserType? Type { get; set; }
|
||||
/// <summary>
|
||||
/// Determines if this member can access all collections within the organization, or only the associated
|
||||
/// collections. If set to <c>true</c>, this option overrides any collection assignments.
|
||||
/// collections. If set to <c>true</c>, this option overrides any collection assignments. If your organization is using
|
||||
/// the latest collection enhancements, you will not be allowed to set this property to <c>true</c>.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? AccessAll { get; set; }
|
||||
/// <summary>
|
||||
/// External identifier for reference or linking this member to another system, such as a user directory.
|
||||
|
@ -93,7 +93,7 @@ public class CollectionsController : Controller
|
||||
}
|
||||
var updatedCollection = model.ToCollection(existingCollection);
|
||||
var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(_currentContext.OrganizationId.Value);
|
||||
var associations = model.Groups?.Select(c => c.ToCollectionAccessSelection(organizationAbility?.FlexibleCollections ?? false));
|
||||
var associations = model.Groups?.Select(c => c.ToCollectionAccessSelection(organizationAbility?.FlexibleCollections ?? false)).ToList();
|
||||
await _collectionService.SaveAsync(updatedCollection, associations);
|
||||
var response = new CollectionResponseModel(updatedCollection, associations);
|
||||
return new JsonResult(response);
|
||||
|
@ -36,10 +36,10 @@ public class CreateGroupCommand : ICreateGroupCommand
|
||||
}
|
||||
|
||||
public async Task CreateGroupAsync(Group group, Organization organization,
|
||||
IEnumerable<CollectionAccessSelection> collections = null,
|
||||
ICollection<CollectionAccessSelection> collections = null,
|
||||
IEnumerable<Guid> users = null)
|
||||
{
|
||||
Validate(organization, group);
|
||||
Validate(organization, group, collections);
|
||||
await GroupRepositoryCreateGroupAsync(group, organization, collections);
|
||||
|
||||
if (users != null)
|
||||
@ -51,10 +51,10 @@ public class CreateGroupCommand : ICreateGroupCommand
|
||||
}
|
||||
|
||||
public async Task CreateGroupAsync(Group group, Organization organization, EventSystemUser systemUser,
|
||||
IEnumerable<CollectionAccessSelection> collections = null,
|
||||
ICollection<CollectionAccessSelection> collections = null,
|
||||
IEnumerable<Guid> users = null)
|
||||
{
|
||||
Validate(organization, group);
|
||||
Validate(organization, group, collections);
|
||||
await GroupRepositoryCreateGroupAsync(group, organization, collections);
|
||||
|
||||
if (users != null)
|
||||
@ -103,7 +103,7 @@ public class CreateGroupCommand : ICreateGroupCommand
|
||||
}
|
||||
}
|
||||
|
||||
private static void Validate(Organization organization, Group group)
|
||||
private static void Validate(Organization organization, Group group, IEnumerable<CollectionAccessSelection> collections)
|
||||
{
|
||||
if (organization == null)
|
||||
{
|
||||
@ -115,9 +115,18 @@ public class CreateGroupCommand : ICreateGroupCommand
|
||||
throw new BadRequestException("This organization cannot use groups.");
|
||||
}
|
||||
|
||||
if (organization.FlexibleCollections && group.AccessAll)
|
||||
if (organization.FlexibleCollections)
|
||||
{
|
||||
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the group to collections instead.");
|
||||
if (group.AccessAll)
|
||||
{
|
||||
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the group to collections instead.");
|
||||
}
|
||||
|
||||
var invalidAssociations = collections?.Where(cas => cas.Manage && (cas.ReadOnly || cas.HidePasswords));
|
||||
if (invalidAssociations?.Any() ?? false)
|
||||
{
|
||||
throw new BadRequestException("The Manage property is mutually exclusive and cannot be true while the ReadOnly or HidePasswords properties are also true.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces;
|
||||
public interface ICreateGroupCommand
|
||||
{
|
||||
Task CreateGroupAsync(Group group, Organization organization,
|
||||
IEnumerable<CollectionAccessSelection> collections = null,
|
||||
ICollection<CollectionAccessSelection> collections = null,
|
||||
IEnumerable<Guid> users = null);
|
||||
|
||||
Task CreateGroupAsync(Group group, Organization organization, EventSystemUser systemUser,
|
||||
IEnumerable<CollectionAccessSelection> collections = null,
|
||||
ICollection<CollectionAccessSelection> collections = null,
|
||||
IEnumerable<Guid> users = null);
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces;
|
||||
public interface IUpdateGroupCommand
|
||||
{
|
||||
Task UpdateGroupAsync(Group group, Organization organization,
|
||||
IEnumerable<CollectionAccessSelection> collections = null,
|
||||
ICollection<CollectionAccessSelection> collections = null,
|
||||
IEnumerable<Guid> users = null);
|
||||
|
||||
Task UpdateGroupAsync(Group group, Organization organization, EventSystemUser systemUser,
|
||||
IEnumerable<CollectionAccessSelection> collections = null,
|
||||
ICollection<CollectionAccessSelection> collections = null,
|
||||
IEnumerable<Guid> users = null);
|
||||
}
|
||||
|
@ -26,10 +26,10 @@ public class UpdateGroupCommand : IUpdateGroupCommand
|
||||
}
|
||||
|
||||
public async Task UpdateGroupAsync(Group group, Organization organization,
|
||||
IEnumerable<CollectionAccessSelection> collections = null,
|
||||
ICollection<CollectionAccessSelection> collections = null,
|
||||
IEnumerable<Guid> userIds = null)
|
||||
{
|
||||
Validate(organization, group);
|
||||
Validate(organization, group, collections);
|
||||
await GroupRepositoryUpdateGroupAsync(group, collections);
|
||||
|
||||
if (userIds != null)
|
||||
@ -41,10 +41,10 @@ public class UpdateGroupCommand : IUpdateGroupCommand
|
||||
}
|
||||
|
||||
public async Task UpdateGroupAsync(Group group, Organization organization, EventSystemUser systemUser,
|
||||
IEnumerable<CollectionAccessSelection> collections = null,
|
||||
ICollection<CollectionAccessSelection> collections = null,
|
||||
IEnumerable<Guid> userIds = null)
|
||||
{
|
||||
Validate(organization, group);
|
||||
Validate(organization, group, collections);
|
||||
await GroupRepositoryUpdateGroupAsync(group, collections);
|
||||
|
||||
if (userIds != null)
|
||||
@ -97,7 +97,7 @@ public class UpdateGroupCommand : IUpdateGroupCommand
|
||||
}
|
||||
}
|
||||
|
||||
private static void Validate(Organization organization, Group group)
|
||||
private static void Validate(Organization organization, Group group, IEnumerable<CollectionAccessSelection> collections)
|
||||
{
|
||||
if (organization == null)
|
||||
{
|
||||
@ -109,9 +109,18 @@ public class UpdateGroupCommand : IUpdateGroupCommand
|
||||
throw new BadRequestException("This organization cannot use groups.");
|
||||
}
|
||||
|
||||
if (organization.FlexibleCollections && group.AccessAll)
|
||||
if (organization.FlexibleCollections)
|
||||
{
|
||||
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the group to collections instead.");
|
||||
if (group.AccessAll)
|
||||
{
|
||||
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the group to collections instead.");
|
||||
}
|
||||
|
||||
var invalidAssociations = collections?.Where(cas => cas.Manage && (cas.ReadOnly || cas.HidePasswords));
|
||||
if (invalidAssociations?.Any() ?? false)
|
||||
{
|
||||
throw new BadRequestException("The Manage property is mutually exclusive and cannot be true while the ReadOnly or HidePasswords properties are also true.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ public interface IOrganizationService
|
||||
Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, EventSystemUser systemUser,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites);
|
||||
Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid? invitingUserId, string email,
|
||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<CollectionAccessSelection> collections, IEnumerable<Guid> groups);
|
||||
OrganizationUserType type, bool accessAll, string externalId, ICollection<CollectionAccessSelection> collections, IEnumerable<Guid> groups);
|
||||
Task<OrganizationUser> InviteUserAsync(Guid organizationId, EventSystemUser systemUser, string email,
|
||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<CollectionAccessSelection> collections, IEnumerable<Guid> groups);
|
||||
Task<IEnumerable<Tuple<OrganizationUser, string>>> ResendInvitesAsync(Guid organizationId, Guid? invitingUserId, IEnumerable<Guid> organizationUsersId);
|
||||
@ -45,7 +45,7 @@ public interface IOrganizationService
|
||||
Guid confirmingUserId, IUserService userService);
|
||||
Task<List<Tuple<OrganizationUser, string>>> ConfirmUsersAsync(Guid organizationId, Dictionary<Guid, string> keys,
|
||||
Guid confirmingUserId, IUserService userService);
|
||||
Task SaveUserAsync(OrganizationUser user, Guid? savingUserId, IEnumerable<CollectionAccessSelection> collections, IEnumerable<Guid> groups);
|
||||
Task SaveUserAsync(OrganizationUser user, Guid? savingUserId, ICollection<CollectionAccessSelection> collections, IEnumerable<Guid> groups);
|
||||
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
||||
Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId);
|
||||
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
||||
|
@ -1370,7 +1370,7 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
public async Task SaveUserAsync(OrganizationUser user, Guid? savingUserId,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups)
|
||||
{
|
||||
if (user.Id.Equals(default(Guid)))
|
||||
@ -1408,6 +1408,15 @@ public class OrganizationService : IOrganizationService
|
||||
{
|
||||
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the user to collections instead.");
|
||||
}
|
||||
|
||||
if (organizationAbility?.FlexibleCollections == true && collections?.Any() == true)
|
||||
{
|
||||
var invalidAssociations = collections.Where(cas => cas.Manage && (cas.ReadOnly || cas.HidePasswords));
|
||||
if (invalidAssociations.Any())
|
||||
{
|
||||
throw new BadRequestException("The Manage property is mutually exclusive and cannot be true while the ReadOnly or HidePasswords properties are also true.");
|
||||
}
|
||||
}
|
||||
// End Flexible Collections
|
||||
|
||||
// Only autoscale (if required) after all validation has passed so that we know it's a valid request before
|
||||
@ -1625,9 +1634,20 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid? invitingUserId, string email,
|
||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<CollectionAccessSelection> collections,
|
||||
OrganizationUserType type, bool accessAll, string externalId, ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups)
|
||||
{
|
||||
// Validate Collection associations if org is using latest collection enhancements
|
||||
var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(organizationId);
|
||||
if (organizationAbility?.FlexibleCollections ?? false)
|
||||
{
|
||||
var invalidAssociations = collections?.Where(cas => cas.Manage && (cas.ReadOnly || cas.HidePasswords));
|
||||
if (invalidAssociations?.Any() ?? false)
|
||||
{
|
||||
throw new BadRequestException("The Manage property is mutually exclusive and cannot be true while the ReadOnly or HidePasswords properties are also true.");
|
||||
}
|
||||
}
|
||||
|
||||
return await SaveUserSendInviteAsync(organizationId, invitingUserId, systemUser: null, email, type, accessAll, externalId, collections, groups);
|
||||
}
|
||||
|
||||
@ -1635,6 +1655,7 @@ public class OrganizationService : IOrganizationService
|
||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups)
|
||||
{
|
||||
// Collection associations validation not required as they are always an empty list - created via system user (scim)
|
||||
return await SaveUserSendInviteAsync(organizationId, invitingUserId: null, systemUser, email, type, accessAll, externalId, collections, groups);
|
||||
}
|
||||
|
||||
|
@ -55,15 +55,25 @@ public class CollectionService : ICollectionService
|
||||
var groupsList = groups?.ToList();
|
||||
var usersList = users?.ToList();
|
||||
|
||||
// If using Flexible Collections - a collection should always have someone with Can Manage permissions
|
||||
if (org.FlexibleCollections && _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1))
|
||||
if (org.FlexibleCollections)
|
||||
{
|
||||
var groupHasManageAccess = groupsList?.Any(g => g.Manage) ?? false;
|
||||
var userHasManageAccess = usersList?.Any(u => u.Manage) ?? false;
|
||||
if (!groupHasManageAccess && !userHasManageAccess && !org.AllowAdminAccessToAllCollectionItems)
|
||||
// Cannot use Manage with ReadOnly/HidePasswords permissions
|
||||
var invalidAssociations = groupsList?.Where(cas => cas.Manage && (cas.ReadOnly || cas.HidePasswords));
|
||||
if (invalidAssociations?.Any() ?? false)
|
||||
{
|
||||
throw new BadRequestException(
|
||||
"At least one member or group must have can manage permission.");
|
||||
throw new BadRequestException("The Manage property is mutually exclusive and cannot be true while the ReadOnly or HidePasswords properties are also true.");
|
||||
}
|
||||
|
||||
// If using Flexible Collections V1 - a collection should always have someone with Can Manage permissions
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1))
|
||||
{
|
||||
var groupHasManageAccess = groupsList?.Any(g => g.Manage) ?? false;
|
||||
var userHasManageAccess = usersList?.Any(u => u.Manage) ?? false;
|
||||
if (!groupHasManageAccess && !userHasManageAccess && !org.AllowAdminAccessToAllCollectionItems)
|
||||
{
|
||||
throw new BadRequestException(
|
||||
"At least one member or group must have can manage permission.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ public class GroupsControllerTests
|
||||
g.OrganizationId == organization.Id && g.Name == groupRequestModel.Name &&
|
||||
g.AccessAll == groupRequestModel.AccessAll),
|
||||
organization,
|
||||
Arg.Any<IEnumerable<CollectionAccessSelection>>(),
|
||||
Arg.Any<ICollection<CollectionAccessSelection>>(),
|
||||
Arg.Any<IEnumerable<Guid>>());
|
||||
Assert.Equal(groupRequestModel.Name, response.Name);
|
||||
Assert.Equal(organization.Id, response.OrganizationId);
|
||||
@ -57,7 +57,7 @@ public class GroupsControllerTests
|
||||
g.OrganizationId == organization.Id && g.Name == groupRequestModel.Name &&
|
||||
g.AccessAll == groupRequestModel.AccessAll),
|
||||
Arg.Is<Organization>(o => o.Id == organization.Id),
|
||||
Arg.Any<IEnumerable<CollectionAccessSelection>>(),
|
||||
Arg.Any<ICollection<CollectionAccessSelection>>(),
|
||||
Arg.Any<IEnumerable<Guid>>());
|
||||
Assert.Equal(groupRequestModel.Name, response.Name);
|
||||
Assert.Equal(organization.Id, response.OrganizationId);
|
||||
|
@ -5,6 +5,7 @@ using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
@ -21,8 +22,15 @@ public class GroupsControllerTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Post_Success(Organization organization, GroupCreateUpdateRequestModel groupRequestModel, SutProvider<GroupsController> sutProvider)
|
||||
public async Task Post_Success_BeforeFlexibleCollectionMigration(Organization organization, GroupCreateUpdateRequestModel groupRequestModel, SutProvider<GroupsController> sutProvider)
|
||||
{
|
||||
// Organization has not migrated
|
||||
organization.FlexibleCollections = false;
|
||||
|
||||
// Permissions do not contain Manage property
|
||||
var expectedPermissions = (groupRequestModel.Collections ?? []).Select(model => new AssociationWithPermissionsRequestModel { Id = model.Id, ReadOnly = model.ReadOnly, HidePasswords = model.HidePasswords.GetValueOrDefault() });
|
||||
groupRequestModel.Collections = expectedPermissions;
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationId.Returns(organization.Id);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
@ -34,7 +42,7 @@ public class GroupsControllerTests
|
||||
g.OrganizationId == organization.Id && g.Name == groupRequestModel.Name &&
|
||||
g.AccessAll == groupRequestModel.AccessAll && g.ExternalId == groupRequestModel.ExternalId),
|
||||
organization,
|
||||
Arg.Any<IEnumerable<CollectionAccessSelection>>());
|
||||
Arg.Any<ICollection<CollectionAccessSelection>>());
|
||||
|
||||
Assert.Equal(groupRequestModel.Name, responseValue.Name);
|
||||
Assert.Equal(groupRequestModel.AccessAll, responseValue.AccessAll);
|
||||
@ -43,8 +51,32 @@ public class GroupsControllerTests
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Put_Success(Organization organization, Group group, GroupCreateUpdateRequestModel groupRequestModel, SutProvider<GroupsController> sutProvider)
|
||||
public async Task Post_Throws_BadRequestException_BeforeFlexibleCollectionMigration_Manage(Organization organization, GroupCreateUpdateRequestModel groupRequestModel, SutProvider<GroupsController> sutProvider)
|
||||
{
|
||||
// Organization has not migrated
|
||||
organization.FlexibleCollections = false;
|
||||
|
||||
// Contains at least one can manage
|
||||
groupRequestModel.Collections.First().Manage = true;
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationId.Returns(organization.Id);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
await sutProvider.GetDependency<ICreateGroupCommand>().DidNotReceiveWithAnyArgs().CreateGroupAsync(default, default, default, default);
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.Post(groupRequestModel));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Put_Success_BeforeFlexibleCollectionMigration(Organization organization, Group group, GroupCreateUpdateRequestModel groupRequestModel, SutProvider<GroupsController> sutProvider)
|
||||
{
|
||||
// Organization has not migrated
|
||||
organization.FlexibleCollections = false;
|
||||
|
||||
// Permissions do not contain Manage property
|
||||
var expectedPermissions = (groupRequestModel.Collections ?? []).Select(model => new AssociationWithPermissionsRequestModel { Id = model.Id, ReadOnly = model.ReadOnly, HidePasswords = model.HidePasswords.GetValueOrDefault() });
|
||||
groupRequestModel.Collections = expectedPermissions;
|
||||
|
||||
group.OrganizationId = organization.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
@ -59,7 +91,86 @@ public class GroupsControllerTests
|
||||
g.OrganizationId == organization.Id && g.Name == groupRequestModel.Name &&
|
||||
g.AccessAll == groupRequestModel.AccessAll && g.ExternalId == groupRequestModel.ExternalId),
|
||||
Arg.Is<Organization>(o => o.Id == organization.Id),
|
||||
Arg.Any<IEnumerable<CollectionAccessSelection>>());
|
||||
Arg.Any<ICollection<CollectionAccessSelection>>());
|
||||
|
||||
Assert.Equal(groupRequestModel.Name, responseValue.Name);
|
||||
Assert.Equal(groupRequestModel.AccessAll, responseValue.AccessAll);
|
||||
Assert.Equal(groupRequestModel.ExternalId, responseValue.ExternalId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Put_Throws_BadRequestException_BeforeFlexibleCollectionMigration_Manage(Organization organization, Group group, GroupCreateUpdateRequestModel groupRequestModel, SutProvider<GroupsController> sutProvider)
|
||||
{
|
||||
// Organization has not migrated
|
||||
organization.FlexibleCollections = false;
|
||||
|
||||
// Contains at least one can manage
|
||||
groupRequestModel.Collections.First().Manage = true;
|
||||
|
||||
group.OrganizationId = organization.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<IGroupRepository>().GetByIdAsync(group.Id).Returns(group);
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationId.Returns(organization.Id);
|
||||
|
||||
await sutProvider.GetDependency<IUpdateGroupCommand>().DidNotReceiveWithAnyArgs().UpdateGroupAsync(default, default, default, default);
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.Put(group.Id, groupRequestModel));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Post_Success_AfterFlexibleCollectionMigration(Organization organization, GroupCreateUpdateRequestModel groupRequestModel, SutProvider<GroupsController> sutProvider)
|
||||
{
|
||||
// Organization has migrated
|
||||
organization.FlexibleCollections = true;
|
||||
|
||||
// Contains at least one can manage
|
||||
groupRequestModel.Collections.First().Manage = true;
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationId.Returns(organization.Id);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
var response = await sutProvider.Sut.Post(groupRequestModel) as JsonResult;
|
||||
var responseValue = response.Value as GroupResponseModel;
|
||||
|
||||
await sutProvider.GetDependency<ICreateGroupCommand>().Received(1).CreateGroupAsync(
|
||||
Arg.Is<Group>(g =>
|
||||
g.OrganizationId == organization.Id && g.Name == groupRequestModel.Name &&
|
||||
g.AccessAll == groupRequestModel.AccessAll && g.ExternalId == groupRequestModel.ExternalId),
|
||||
organization,
|
||||
Arg.Any<ICollection<CollectionAccessSelection>>());
|
||||
|
||||
Assert.Equal(groupRequestModel.Name, responseValue.Name);
|
||||
Assert.Equal(groupRequestModel.AccessAll, responseValue.AccessAll);
|
||||
Assert.Equal(groupRequestModel.ExternalId, responseValue.ExternalId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Put_Success_AfterFlexibleCollectionMigration(Organization organization, Group group, GroupCreateUpdateRequestModel groupRequestModel, SutProvider<GroupsController> sutProvider)
|
||||
{
|
||||
// Organization has migrated
|
||||
organization.FlexibleCollections = true;
|
||||
|
||||
// Contains at least one can manage
|
||||
groupRequestModel.Collections.First().Manage = true;
|
||||
|
||||
group.OrganizationId = organization.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<IGroupRepository>().GetByIdAsync(group.Id).Returns(group);
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationId.Returns(organization.Id);
|
||||
|
||||
var response = await sutProvider.Sut.Put(group.Id, groupRequestModel) as JsonResult;
|
||||
var responseValue = response.Value as GroupResponseModel;
|
||||
|
||||
await sutProvider.GetDependency<IUpdateGroupCommand>().Received(1).UpdateGroupAsync(
|
||||
Arg.Is<Group>(g =>
|
||||
g.OrganizationId == organization.Id && g.Name == groupRequestModel.Name &&
|
||||
g.AccessAll == groupRequestModel.AccessAll && g.ExternalId == groupRequestModel.ExternalId),
|
||||
Arg.Is<Organization>(o => o.Id == organization.Id),
|
||||
Arg.Any<ICollection<CollectionAccessSelection>>());
|
||||
|
||||
Assert.Equal(groupRequestModel.Name, responseValue.Name);
|
||||
Assert.Equal(groupRequestModel.AccessAll, responseValue.AccessAll);
|
||||
|
@ -1089,7 +1089,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveUser_NoUserId_Throws(OrganizationUser user, Guid? savingUserId,
|
||||
IEnumerable<CollectionAccessSelection> collections, IEnumerable<Guid> groups, SutProvider<OrganizationService> sutProvider)
|
||||
ICollection<CollectionAccessSelection> collections, IEnumerable<Guid> groups, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
user.Id = default(Guid);
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
@ -1099,7 +1099,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveUser_NoChangeToData_Throws(OrganizationUser user, Guid? savingUserId,
|
||||
IEnumerable<CollectionAccessSelection> collections, IEnumerable<Guid> groups, SutProvider<OrganizationService> sutProvider)
|
||||
ICollection<CollectionAccessSelection> collections, IEnumerable<Guid> groups, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
organizationUserRepository.GetByIdAsync(user.Id).Returns(user);
|
||||
@ -1113,7 +1113,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
Organization organization,
|
||||
OrganizationUser oldUserData,
|
||||
OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
Permissions permissions,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
||||
@ -1145,7 +1145,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
Organization organization,
|
||||
OrganizationUser oldUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
@ -1182,7 +1182,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
Organization organization,
|
||||
OrganizationUser oldUserData,
|
||||
OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
Permissions permissions,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
||||
@ -1217,7 +1217,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
Organization organization,
|
||||
OrganizationUser oldUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
Permissions permissions,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
||||
@ -1251,7 +1251,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
Organization organization,
|
||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser savingUser,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationOwner,
|
||||
@ -1295,7 +1295,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
Organization organization,
|
||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser savingUser,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
@ -1330,7 +1330,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
Organization organization,
|
||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser oldUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser newUserData,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
@ -1365,7 +1365,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Manager)] OrganizationUser newUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner, status: OrganizationUserStatusType.Confirmed)] OrganizationUser savingUser,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
@ -1403,7 +1403,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser newUserData,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner, status: OrganizationUserStatusType.Confirmed)] OrganizationUser savingUser,
|
||||
IEnumerable<CollectionAccessSelection> collections,
|
||||
ICollection<CollectionAccessSelection> collections,
|
||||
IEnumerable<Guid> groups,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user