mirror of
https://github.com/bitwarden/server.git
synced 2025-02-01 23:31:41 +01:00
[PM-3176] Extract IOrganizationService.SaveUserAsync to a command (#3894)
* [PM-3176] Extract IOrganizationService.SaveUserAsync to a command * [PM-3176] Enabled nullable on command * [PM-3176] Removed check that was not working
This commit is contained in:
parent
6672019122
commit
92716fe319
@ -38,6 +38,7 @@ public class OrganizationUsersController : Controller
|
|||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery;
|
private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery;
|
||||||
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
|
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
|
||||||
|
private readonly IUpdateOrganizationUserCommand _updateOrganizationUserCommand;
|
||||||
private readonly IUpdateOrganizationUserGroupsCommand _updateOrganizationUserGroupsCommand;
|
private readonly IUpdateOrganizationUserGroupsCommand _updateOrganizationUserGroupsCommand;
|
||||||
private readonly IAcceptOrgUserCommand _acceptOrgUserCommand;
|
private readonly IAcceptOrgUserCommand _acceptOrgUserCommand;
|
||||||
private readonly IAuthorizationService _authorizationService;
|
private readonly IAuthorizationService _authorizationService;
|
||||||
@ -55,6 +56,7 @@ public class OrganizationUsersController : Controller
|
|||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery,
|
ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery,
|
||||||
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
||||||
|
IUpdateOrganizationUserCommand updateOrganizationUserCommand,
|
||||||
IUpdateOrganizationUserGroupsCommand updateOrganizationUserGroupsCommand,
|
IUpdateOrganizationUserGroupsCommand updateOrganizationUserGroupsCommand,
|
||||||
IAcceptOrgUserCommand acceptOrgUserCommand,
|
IAcceptOrgUserCommand acceptOrgUserCommand,
|
||||||
IAuthorizationService authorizationService,
|
IAuthorizationService authorizationService,
|
||||||
@ -71,6 +73,7 @@ public class OrganizationUsersController : Controller
|
|||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery;
|
_countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery;
|
||||||
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
||||||
|
_updateOrganizationUserCommand = updateOrganizationUserCommand;
|
||||||
_updateOrganizationUserGroupsCommand = updateOrganizationUserGroupsCommand;
|
_updateOrganizationUserGroupsCommand = updateOrganizationUserGroupsCommand;
|
||||||
_acceptOrgUserCommand = acceptOrgUserCommand;
|
_acceptOrgUserCommand = acceptOrgUserCommand;
|
||||||
_authorizationService = authorizationService;
|
_authorizationService = authorizationService;
|
||||||
@ -338,7 +341,7 @@ public class OrganizationUsersController : Controller
|
|||||||
? null
|
? null
|
||||||
: model.Groups;
|
: model.Groups;
|
||||||
|
|
||||||
await _organizationService.SaveUserAsync(model.ToOrganizationUser(organizationUser), userId,
|
await _updateOrganizationUserCommand.UpdateUserAsync(model.ToOrganizationUser(organizationUser), userId,
|
||||||
model.Collections?.Select(c => c.ToSelectionReadOnly()).ToList(), groups);
|
model.Collections?.Select(c => c.ToSelectionReadOnly()).ToList(), groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ public class MembersController : Controller
|
|||||||
private readonly IOrganizationService _organizationService;
|
private readonly IOrganizationService _organizationService;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
|
private readonly IUpdateOrganizationUserCommand _updateOrganizationUserCommand;
|
||||||
private readonly IUpdateOrganizationUserGroupsCommand _updateOrganizationUserGroupsCommand;
|
private readonly IUpdateOrganizationUserGroupsCommand _updateOrganizationUserGroupsCommand;
|
||||||
private readonly IApplicationCacheService _applicationCacheService;
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ public class MembersController : Controller
|
|||||||
IOrganizationService organizationService,
|
IOrganizationService organizationService,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
|
IUpdateOrganizationUserCommand updateOrganizationUserCommand,
|
||||||
IUpdateOrganizationUserGroupsCommand updateOrganizationUserGroupsCommand,
|
IUpdateOrganizationUserGroupsCommand updateOrganizationUserGroupsCommand,
|
||||||
IApplicationCacheService applicationCacheService)
|
IApplicationCacheService applicationCacheService)
|
||||||
{
|
{
|
||||||
@ -38,6 +40,7 @@ public class MembersController : Controller
|
|||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
|
_updateOrganizationUserCommand = updateOrganizationUserCommand;
|
||||||
_updateOrganizationUserGroupsCommand = updateOrganizationUserGroupsCommand;
|
_updateOrganizationUserGroupsCommand = updateOrganizationUserGroupsCommand;
|
||||||
_applicationCacheService = applicationCacheService;
|
_applicationCacheService = applicationCacheService;
|
||||||
}
|
}
|
||||||
@ -154,7 +157,7 @@ public class MembersController : Controller
|
|||||||
var updatedUser = model.ToOrganizationUser(existingUser);
|
var updatedUser = model.ToOrganizationUser(existingUser);
|
||||||
var flexibleCollectionsIsEnabled = await FlexibleCollectionsIsEnabledAsync(_currentContext.OrganizationId.Value);
|
var flexibleCollectionsIsEnabled = await FlexibleCollectionsIsEnabledAsync(_currentContext.OrganizationId.Value);
|
||||||
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(flexibleCollectionsIsEnabled)).ToList();
|
var associations = model.Collections?.Select(c => c.ToCollectionAccessSelection(flexibleCollectionsIsEnabled)).ToList();
|
||||||
await _organizationService.SaveUserAsync(updatedUser, null, associations, model.Groups);
|
await _updateOrganizationUserCommand.UpdateUserAsync(updatedUser, null, associations, model.Groups);
|
||||||
MemberResponseModel response = null;
|
MemberResponseModel response = null;
|
||||||
if (existingUser.UserId.HasValue)
|
if (existingUser.UserId.HasValue)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
#nullable enable
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
|
||||||
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
|
||||||
|
public interface IUpdateOrganizationUserCommand
|
||||||
|
{
|
||||||
|
Task UpdateUserAsync(OrganizationUser user, Guid? savingUserId, IEnumerable<CollectionAccessSelection> collections, IEnumerable<Guid>? groups);
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
#nullable enable
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Business;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||||
|
|
||||||
|
public class UpdateOrganizationUserCommand : IUpdateOrganizationUserCommand
|
||||||
|
{
|
||||||
|
private readonly IEventService _eventService;
|
||||||
|
private readonly IOrganizationService _organizationService;
|
||||||
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery;
|
||||||
|
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
|
||||||
|
|
||||||
|
public UpdateOrganizationUserCommand(
|
||||||
|
IEventService eventService,
|
||||||
|
IOrganizationService organizationService,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery,
|
||||||
|
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand)
|
||||||
|
{
|
||||||
|
_eventService = eventService;
|
||||||
|
_organizationService = organizationService;
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
|
_organizationUserRepository = organizationUserRepository;
|
||||||
|
_countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery;
|
||||||
|
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateUserAsync(OrganizationUser user, Guid? savingUserId,
|
||||||
|
IEnumerable<CollectionAccessSelection> collections, IEnumerable<Guid>? groups)
|
||||||
|
{
|
||||||
|
if (user.Id.Equals(default(Guid)))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Invite the user first.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalUser = await _organizationUserRepository.GetByIdAsync(user.Id);
|
||||||
|
|
||||||
|
if (savingUserId.HasValue)
|
||||||
|
{
|
||||||
|
await _organizationService.ValidateOrganizationUserUpdatePermissions(user.OrganizationId, user.Type, originalUser.Type, user.GetPermissions());
|
||||||
|
}
|
||||||
|
|
||||||
|
await _organizationService.ValidateOrganizationCustomPermissionsEnabledAsync(user.OrganizationId, user.Type);
|
||||||
|
|
||||||
|
if (user.Type != OrganizationUserType.Owner &&
|
||||||
|
!await _organizationService.HasConfirmedOwnersExceptAsync(user.OrganizationId, new[] { user.Id }))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the organization is using Flexible Collections, prevent use of any deprecated permissions
|
||||||
|
var organization = await _organizationRepository.GetByIdAsync(user.OrganizationId);
|
||||||
|
if (organization.FlexibleCollections && user.Type == OrganizationUserType.Manager)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("The Manager role has been deprecated by collection enhancements. Use the collection Can Manage permission instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (organization.FlexibleCollections && user.AccessAll)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the user to collections instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (organization.FlexibleCollections && 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
|
||||||
|
// updating Stripe
|
||||||
|
if (!originalUser.AccessSecretsManager && user.AccessSecretsManager)
|
||||||
|
{
|
||||||
|
var additionalSmSeatsRequired = await _countNewSmSeatsRequiredQuery.CountNewSmSeatsRequiredAsync(user.OrganizationId, 1);
|
||||||
|
if (additionalSmSeatsRequired > 0)
|
||||||
|
{
|
||||||
|
var update = new SecretsManagerSubscriptionUpdate(organization, true)
|
||||||
|
.AdjustSeats(additionalSmSeatsRequired);
|
||||||
|
await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.AccessAll)
|
||||||
|
{
|
||||||
|
// We don't need any collections if we're flagged to have all access.
|
||||||
|
collections = new List<CollectionAccessSelection>();
|
||||||
|
}
|
||||||
|
await _organizationUserRepository.ReplaceAsync(user, collections);
|
||||||
|
|
||||||
|
if (groups != null)
|
||||||
|
{
|
||||||
|
await _organizationUserRepository.UpdateGroupsAsync(user.Id, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _eventService.LogOrganizationUserEventAsync(user, EventType.OrganizationUser_Updated);
|
||||||
|
}
|
||||||
|
}
|
@ -56,7 +56,6 @@ public interface IOrganizationService
|
|||||||
Guid confirmingUserId, IUserService userService);
|
Guid confirmingUserId, IUserService userService);
|
||||||
Task<List<Tuple<OrganizationUser, string>>> ConfirmUsersAsync(Guid organizationId, Dictionary<Guid, string> keys,
|
Task<List<Tuple<OrganizationUser, string>>> ConfirmUsersAsync(Guid organizationId, Dictionary<Guid, string> keys,
|
||||||
Guid confirmingUserId, IUserService userService);
|
Guid confirmingUserId, IUserService userService);
|
||||||
Task SaveUserAsync(OrganizationUser user, Guid? savingUserId, ICollection<CollectionAccessSelection> collections, IEnumerable<Guid> groups);
|
|
||||||
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
||||||
Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId);
|
Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId);
|
||||||
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
||||||
@ -93,4 +92,5 @@ public interface IOrganizationService
|
|||||||
void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade);
|
void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade);
|
||||||
Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType,
|
Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType,
|
||||||
OrganizationUserType? oldType, Permissions permissions);
|
OrganizationUserType? oldType, Permissions permissions);
|
||||||
|
Task ValidateOrganizationCustomPermissionsEnabledAsync(Guid organizationId, OrganizationUserType newType);
|
||||||
}
|
}
|
||||||
|
@ -1467,84 +1467,6 @@ public class OrganizationService : IOrganizationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveUserAsync(OrganizationUser user, Guid? savingUserId,
|
|
||||||
ICollection<CollectionAccessSelection> collections,
|
|
||||||
IEnumerable<Guid> groups)
|
|
||||||
{
|
|
||||||
if (user.Id.Equals(default(Guid)))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Invite the user first.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var originalUser = await _organizationUserRepository.GetByIdAsync(user.Id);
|
|
||||||
if (user.Equals(originalUser))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Please make changes before saving.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savingUserId.HasValue)
|
|
||||||
{
|
|
||||||
await ValidateOrganizationUserUpdatePermissions(user.OrganizationId, user.Type, originalUser.Type, user.GetPermissions());
|
|
||||||
}
|
|
||||||
|
|
||||||
await ValidateOrganizationCustomPermissionsEnabledAsync(user.OrganizationId, user.Type);
|
|
||||||
|
|
||||||
if (user.Type != OrganizationUserType.Owner &&
|
|
||||||
!await HasConfirmedOwnersExceptAsync(user.OrganizationId, new[] { user.Id }))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the organization is using Flexible Collections, prevent use of any deprecated permissions
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(user.OrganizationId);
|
|
||||||
if (organization.FlexibleCollections && user.Type == OrganizationUserType.Manager)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("The Manager role has been deprecated by collection enhancements. Use the collection Can Manage permission instead.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (organization.FlexibleCollections && user.AccessAll)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the user to collections instead.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (organization.FlexibleCollections && 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
|
|
||||||
// updating Stripe
|
|
||||||
if (!originalUser.AccessSecretsManager && user.AccessSecretsManager)
|
|
||||||
{
|
|
||||||
var additionalSmSeatsRequired = await _countNewSmSeatsRequiredQuery.CountNewSmSeatsRequiredAsync(user.OrganizationId, 1);
|
|
||||||
if (additionalSmSeatsRequired > 0)
|
|
||||||
{
|
|
||||||
var update = new SecretsManagerSubscriptionUpdate(organization, true)
|
|
||||||
.AdjustSeats(additionalSmSeatsRequired);
|
|
||||||
await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(update);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.AccessAll)
|
|
||||||
{
|
|
||||||
// We don't need any collections if we're flagged to have all access.
|
|
||||||
collections = new List<CollectionAccessSelection>();
|
|
||||||
}
|
|
||||||
await _organizationUserRepository.ReplaceAsync(user, collections);
|
|
||||||
|
|
||||||
if (groups != null)
|
|
||||||
{
|
|
||||||
await _organizationUserRepository.UpdateGroupsAsync(user.Id, groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _eventService.LogOrganizationUserEventAsync(user, EventType.OrganizationUser_Updated);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
||||||
public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
||||||
{
|
{
|
||||||
@ -2182,7 +2104,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ValidateOrganizationCustomPermissionsEnabledAsync(Guid organizationId, OrganizationUserType newType)
|
public async Task ValidateOrganizationCustomPermissionsEnabledAsync(Guid organizationId, OrganizationUserType newType)
|
||||||
{
|
{
|
||||||
if (newType != OrganizationUserType.Custom)
|
if (newType != OrganizationUserType.Custom)
|
||||||
{
|
{
|
||||||
|
@ -90,6 +90,7 @@ public static class OrganizationServiceCollectionExtensions
|
|||||||
private static void AddOrganizationUserCommands(this IServiceCollection services)
|
private static void AddOrganizationUserCommands(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddScoped<IDeleteOrganizationUserCommand, DeleteOrganizationUserCommand>();
|
services.AddScoped<IDeleteOrganizationUserCommand, DeleteOrganizationUserCommand>();
|
||||||
|
services.AddScoped<IUpdateOrganizationUserCommand, UpdateOrganizationUserCommand>();
|
||||||
services.AddScoped<IUpdateOrganizationUserGroupsCommand, UpdateOrganizationUserGroupsCommand>();
|
services.AddScoped<IUpdateOrganizationUserGroupsCommand, UpdateOrganizationUserGroupsCommand>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using Bit.Core;
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Enums;
|
using Bit.Core.AdminConsole.Enums;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
@ -121,7 +122,7 @@ public class OrganizationUsersControllerTests
|
|||||||
|
|
||||||
await sutProvider.Sut.Put(orgId, organizationUser.Id, model);
|
await sutProvider.Sut.Put(orgId, organizationUser.Id, model);
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>().Received(1).SaveUserAsync(Arg.Is<OrganizationUser>(ou =>
|
await sutProvider.GetDependency<IUpdateOrganizationUserCommand>().Received(1).UpdateUserAsync(Arg.Is<OrganizationUser>(ou =>
|
||||||
ou.Type == model.Type &&
|
ou.Type == model.Type &&
|
||||||
ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) &&
|
ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) &&
|
||||||
ou.AccessSecretsManager == model.AccessSecretsManager &&
|
ou.AccessSecretsManager == model.AccessSecretsManager &&
|
||||||
@ -157,7 +158,7 @@ public class OrganizationUsersControllerTests
|
|||||||
|
|
||||||
await sutProvider.Sut.Put(orgId, organizationUser.Id, model);
|
await sutProvider.Sut.Put(orgId, organizationUser.Id, model);
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>().Received(1).SaveUserAsync(Arg.Is<OrganizationUser>(ou =>
|
await sutProvider.GetDependency<IUpdateOrganizationUserCommand>().Received(1).UpdateUserAsync(Arg.Is<OrganizationUser>(ou =>
|
||||||
ou.Type == model.Type &&
|
ou.Type == model.Type &&
|
||||||
ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) &&
|
ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) &&
|
||||||
ou.AccessSecretsManager == model.AccessSecretsManager &&
|
ou.AccessSecretsManager == model.AccessSecretsManager &&
|
||||||
@ -193,7 +194,7 @@ public class OrganizationUsersControllerTests
|
|||||||
|
|
||||||
await sutProvider.Sut.Put(orgId, organizationUser.Id, model);
|
await sutProvider.Sut.Put(orgId, organizationUser.Id, model);
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>().Received(1).SaveUserAsync(Arg.Is<OrganizationUser>(ou =>
|
await sutProvider.GetDependency<IUpdateOrganizationUserCommand>().Received(1).UpdateUserAsync(Arg.Is<OrganizationUser>(ou =>
|
||||||
ou.Type == model.Type &&
|
ou.Type == model.Type &&
|
||||||
ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) &&
|
ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) &&
|
||||||
ou.AccessSecretsManager == model.AccessSecretsManager &&
|
ou.AccessSecretsManager == model.AccessSecretsManager &&
|
||||||
|
@ -0,0 +1,158 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Test.AutoFixture.OrganizationUserFixtures;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
public class UpdateOrganizationUserCommandTests
|
||||||
|
{
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task UpdateUserAsync_NoUserId_Throws(OrganizationUser user, Guid? savingUserId,
|
||||||
|
ICollection<CollectionAccessSelection> collections, IEnumerable<Guid> groups, SutProvider<UpdateOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
user.Id = default(Guid);
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.UpdateUserAsync(user, savingUserId, collections, groups));
|
||||||
|
Assert.Contains("invite the user first", exception.Message.ToLowerInvariant());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task UpdateUserAsync_Passes(
|
||||||
|
Organization organization,
|
||||||
|
OrganizationUser oldUserData,
|
||||||
|
OrganizationUser newUserData,
|
||||||
|
ICollection<CollectionAccessSelection> collections,
|
||||||
|
IEnumerable<Guid> groups,
|
||||||
|
Permissions permissions,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
||||||
|
SutProvider<UpdateOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
var organizationService = sutProvider.GetDependency<IOrganizationService>();
|
||||||
|
|
||||||
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
|
||||||
|
newUserData.Id = oldUserData.Id;
|
||||||
|
newUserData.UserId = oldUserData.UserId;
|
||||||
|
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
||||||
|
newUserData.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
});
|
||||||
|
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
||||||
|
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
||||||
|
.Returns(new List<OrganizationUser> { savingUser });
|
||||||
|
organizationService
|
||||||
|
.HasConfirmedOwnersExceptAsync(
|
||||||
|
newUserData.OrganizationId,
|
||||||
|
Arg.Is<IEnumerable<Guid>>(i => i.Contains(newUserData.Id)))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
await sutProvider.Sut.UpdateUserAsync(newUserData, savingUser.UserId, collections, groups);
|
||||||
|
|
||||||
|
await organizationService.Received(1).ValidateOrganizationUserUpdatePermissions(
|
||||||
|
newUserData.OrganizationId,
|
||||||
|
newUserData.Type,
|
||||||
|
oldUserData.Type,
|
||||||
|
Arg.Any<Permissions>());
|
||||||
|
await organizationService.Received(1).ValidateOrganizationCustomPermissionsEnabledAsync(
|
||||||
|
newUserData.OrganizationId,
|
||||||
|
newUserData.Type);
|
||||||
|
await organizationService.Received(1).HasConfirmedOwnersExceptAsync(
|
||||||
|
newUserData.OrganizationId,
|
||||||
|
Arg.Is<IEnumerable<Guid>>(i => i.Contains(newUserData.Id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task UpdateUserAsync_WithFlexibleCollections_WhenUpgradingToManager_Throws(
|
||||||
|
Organization organization,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Manager)] OrganizationUser newUserData,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Owner, status: OrganizationUserStatusType.Confirmed)] OrganizationUser savingUser,
|
||||||
|
ICollection<CollectionAccessSelection> collections,
|
||||||
|
IEnumerable<Guid> groups,
|
||||||
|
SutProvider<UpdateOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
organization.FlexibleCollections = true;
|
||||||
|
newUserData.Id = oldUserData.Id;
|
||||||
|
newUserData.UserId = oldUserData.UserId;
|
||||||
|
newUserData.OrganizationId = oldUserData.OrganizationId = savingUser.OrganizationId = organization.Id;
|
||||||
|
newUserData.Permissions = CoreHelpers.ClassToJsonData(new Permissions());
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(organization.Id)
|
||||||
|
.Returns(organization);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationService>()
|
||||||
|
.HasConfirmedOwnersExceptAsync(newUserData.OrganizationId, Arg.Is<IEnumerable<Guid>>(i => i.Contains(newUserData.Id)))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetByIdAsync(oldUserData.Id)
|
||||||
|
.Returns(oldUserData);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||||
|
.Returns(new List<OrganizationUser> { savingUser });
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.UpdateUserAsync(newUserData, oldUserData.UserId, collections, groups));
|
||||||
|
|
||||||
|
Assert.Contains("manager role has been deprecated", exception.Message.ToLowerInvariant());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task UpdateUserAsync_WithFlexibleCollections_WithAccessAll_Throws(
|
||||||
|
Organization organization,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser newUserData,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Owner, status: OrganizationUserStatusType.Confirmed)] OrganizationUser savingUser,
|
||||||
|
ICollection<CollectionAccessSelection> collections,
|
||||||
|
IEnumerable<Guid> groups,
|
||||||
|
SutProvider<UpdateOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
organization.FlexibleCollections = true;
|
||||||
|
newUserData.Id = oldUserData.Id;
|
||||||
|
newUserData.UserId = oldUserData.UserId;
|
||||||
|
newUserData.OrganizationId = oldUserData.OrganizationId = savingUser.OrganizationId = organization.Id;
|
||||||
|
newUserData.Permissions = CoreHelpers.ClassToJsonData(new Permissions());
|
||||||
|
newUserData.AccessAll = true;
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(organization.Id)
|
||||||
|
.Returns(organization);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationService>()
|
||||||
|
.HasConfirmedOwnersExceptAsync(
|
||||||
|
newUserData.OrganizationId,
|
||||||
|
Arg.Is<IEnumerable<Guid>>(i => i.Contains(newUserData.Id)))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetByIdAsync(oldUserData.Id)
|
||||||
|
.Returns(oldUserData);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||||
|
.Returns(new List<OrganizationUser> { savingUser });
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.UpdateUserAsync(newUserData, oldUserData.UserId, collections, groups));
|
||||||
|
|
||||||
|
Assert.Contains("the accessall property has been deprecated", exception.Message.ToLowerInvariant());
|
||||||
|
}
|
||||||
|
}
|
@ -1137,355 +1137,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveUser_NoUserId_Throws(OrganizationUser user, Guid? savingUserId,
|
|
||||||
ICollection<CollectionAccessSelection> collections, IEnumerable<Guid> groups, SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
user.Id = default(Guid);
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveUserAsync(user, savingUserId, collections, groups));
|
|
||||||
Assert.Contains("invite the user first", exception.Message.ToLowerInvariant());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveUser_NoChangeToData_Throws(OrganizationUser user, Guid? savingUserId,
|
|
||||||
ICollection<CollectionAccessSelection> collections, IEnumerable<Guid> groups, SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
organizationUserRepository.GetByIdAsync(user.Id).Returns(user);
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveUserAsync(user, savingUserId, collections, groups));
|
|
||||||
Assert.Contains("make changes before saving", exception.Message.ToLowerInvariant());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveUser_Passes(
|
|
||||||
Organization organization,
|
|
||||||
OrganizationUser oldUserData,
|
|
||||||
OrganizationUser newUserData,
|
|
||||||
ICollection<CollectionAccessSelection> collections,
|
|
||||||
IEnumerable<Guid> groups,
|
|
||||||
Permissions permissions,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
|
|
||||||
newUserData.Id = oldUserData.Id;
|
|
||||||
newUserData.UserId = oldUserData.UserId;
|
|
||||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
|
||||||
newUserData.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
});
|
|
||||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser> { savingUser });
|
|
||||||
currentContext.OrganizationOwner(savingUser.OrganizationId).Returns(true);
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveUserAsync(newUserData, savingUser.UserId, collections, groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveUser_WithCustomType_WhenUseCustomPermissionsIsFalse_Throws(
|
|
||||||
Organization organization,
|
|
||||||
OrganizationUser oldUserData,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
|
||||||
ICollection<CollectionAccessSelection> collections,
|
|
||||||
IEnumerable<Guid> groups,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
organization.UseCustomPermissions = false;
|
|
||||||
|
|
||||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
|
|
||||||
newUserData.Id = oldUserData.Id;
|
|
||||||
newUserData.UserId = oldUserData.UserId;
|
|
||||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
|
||||||
newUserData.Permissions = null;
|
|
||||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser> { savingUser });
|
|
||||||
currentContext.OrganizationOwner(savingUser.OrganizationId).Returns(true);
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveUserAsync(newUserData, savingUser.UserId, collections, groups));
|
|
||||||
Assert.Contains("to enable custom permissions", exception.Message.ToLowerInvariant());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(OrganizationUserType.Admin)]
|
|
||||||
[BitAutoData(OrganizationUserType.Manager)]
|
|
||||||
[BitAutoData(OrganizationUserType.Owner)]
|
|
||||||
[BitAutoData(OrganizationUserType.User)]
|
|
||||||
public async Task SaveUser_WithNonCustomType_WhenUseCustomPermissionsIsFalse_Passes(
|
|
||||||
OrganizationUserType newUserType,
|
|
||||||
Organization organization,
|
|
||||||
OrganizationUser oldUserData,
|
|
||||||
OrganizationUser newUserData,
|
|
||||||
ICollection<CollectionAccessSelection> collections,
|
|
||||||
IEnumerable<Guid> groups,
|
|
||||||
Permissions permissions,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
organization.UseCustomPermissions = false;
|
|
||||||
|
|
||||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
|
|
||||||
newUserData.Id = oldUserData.Id;
|
|
||||||
newUserData.UserId = oldUserData.UserId;
|
|
||||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
|
||||||
newUserData.Type = newUserType;
|
|
||||||
newUserData.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
});
|
|
||||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser> { savingUser });
|
|
||||||
currentContext.OrganizationOwner(savingUser.OrganizationId).Returns(true);
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveUserAsync(newUserData, savingUser.UserId, collections, groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveUser_WithCustomType_WhenUseCustomPermissionsIsTrue_Passes(
|
|
||||||
Organization organization,
|
|
||||||
OrganizationUser oldUserData,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
|
||||||
ICollection<CollectionAccessSelection> collections,
|
|
||||||
IEnumerable<Guid> groups,
|
|
||||||
Permissions permissions,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
organization.UseCustomPermissions = true;
|
|
||||||
|
|
||||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
|
|
||||||
newUserData.Id = oldUserData.Id;
|
|
||||||
newUserData.UserId = oldUserData.UserId;
|
|
||||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
|
||||||
newUserData.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
});
|
|
||||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser> { savingUser });
|
|
||||||
currentContext.OrganizationOwner(savingUser.OrganizationId).Returns(true);
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveUserAsync(newUserData, savingUser.UserId, collections, groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveUser_WithCustomPermission_WhenSavingUserHasCustomPermission_Passes(
|
|
||||||
Organization organization,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
|
||||||
ICollection<CollectionAccessSelection> collections,
|
|
||||||
IEnumerable<Guid> groups,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser savingUser,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationOwner,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
organization.UseCustomPermissions = true;
|
|
||||||
|
|
||||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
|
|
||||||
newUserData.Id = oldUserData.Id;
|
|
||||||
newUserData.UserId = oldUserData.UserId;
|
|
||||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organizationOwner.OrganizationId = organization.Id;
|
|
||||||
newUserData.Permissions = JsonSerializer.Serialize(new Permissions { AccessReports = true }, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
});
|
|
||||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser> { organizationOwner });
|
|
||||||
currentContext.OrganizationCustom(savingUser.OrganizationId).Returns(true);
|
|
||||||
currentContext.ManageUsers(savingUser.OrganizationId).Returns(true);
|
|
||||||
currentContext.AccessReports(savingUser.OrganizationId).Returns(true);
|
|
||||||
currentContext.GetOrganization(savingUser.OrganizationId).Returns(
|
|
||||||
new CurrentContextOrganization()
|
|
||||||
{
|
|
||||||
Permissions = new Permissions
|
|
||||||
{
|
|
||||||
AccessReports = true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveUserAsync(newUserData, savingUser.UserId, collections, groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveUser_WithCustomPermission_WhenSavingUserDoesNotHaveCustomPermission_Throws(
|
|
||||||
Organization organization,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser newUserData,
|
|
||||||
ICollection<CollectionAccessSelection> collections,
|
|
||||||
IEnumerable<Guid> groups,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser savingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
organization.UseCustomPermissions = true;
|
|
||||||
|
|
||||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
|
|
||||||
newUserData.Id = oldUserData.Id;
|
|
||||||
newUserData.UserId = oldUserData.UserId;
|
|
||||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
|
||||||
newUserData.Permissions = JsonSerializer.Serialize(new Permissions { AccessReports = true }, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
});
|
|
||||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
|
||||||
currentContext.OrganizationCustom(savingUser.OrganizationId).Returns(true);
|
|
||||||
currentContext.ManageUsers(savingUser.OrganizationId).Returns(true);
|
|
||||||
currentContext.AccessReports(savingUser.OrganizationId).Returns(false);
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveUserAsync(newUserData, savingUser.UserId, collections, groups));
|
|
||||||
Assert.Contains("custom users can only grant the same custom permissions that they have", exception.Message.ToLowerInvariant());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveUser_WithCustomPermission_WhenUpgradingToAdmin_Throws(
|
|
||||||
Organization organization,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser oldUserData,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser newUserData,
|
|
||||||
ICollection<CollectionAccessSelection> collections,
|
|
||||||
IEnumerable<Guid> groups,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
organization.UseCustomPermissions = true;
|
|
||||||
|
|
||||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
|
|
||||||
newUserData.Id = oldUserData.Id;
|
|
||||||
newUserData.UserId = oldUserData.UserId;
|
|
||||||
newUserData.OrganizationId = oldUserData.OrganizationId = organization.Id;
|
|
||||||
newUserData.Permissions = JsonSerializer.Serialize(new Permissions { AccessReports = true }, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
});
|
|
||||||
organizationUserRepository.GetByIdAsync(oldUserData.Id).Returns(oldUserData);
|
|
||||||
currentContext.OrganizationCustom(oldUserData.OrganizationId).Returns(true);
|
|
||||||
currentContext.ManageUsers(oldUserData.OrganizationId).Returns(true);
|
|
||||||
currentContext.AccessReports(oldUserData.OrganizationId).Returns(false);
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveUserAsync(newUserData, oldUserData.UserId, collections, groups));
|
|
||||||
Assert.Contains("custom users can not manage admins or owners", exception.Message.ToLowerInvariant());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveUser_WithFlexibleCollections_WhenUpgradingToManager_Throws(
|
|
||||||
Organization organization,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Manager)] OrganizationUser newUserData,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner, status: OrganizationUserStatusType.Confirmed)] OrganizationUser savingUser,
|
|
||||||
ICollection<CollectionAccessSelection> collections,
|
|
||||||
IEnumerable<Guid> groups,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
organization.FlexibleCollections = true;
|
|
||||||
newUserData.Id = oldUserData.Id;
|
|
||||||
newUserData.UserId = oldUserData.UserId;
|
|
||||||
newUserData.OrganizationId = oldUserData.OrganizationId = savingUser.OrganizationId = organization.Id;
|
|
||||||
newUserData.Permissions = CoreHelpers.ClassToJsonData(new Permissions());
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
|
||||||
.GetByIdAsync(organization.Id)
|
|
||||||
.Returns(organization);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>()
|
|
||||||
.ManageUsers(organization.Id)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetByIdAsync(oldUserData.Id)
|
|
||||||
.Returns(oldUserData);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser> { savingUser });
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveUserAsync(newUserData, oldUserData.UserId, collections, groups));
|
|
||||||
|
|
||||||
Assert.Contains("manager role has been deprecated", exception.Message.ToLowerInvariant());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveUser_WithFlexibleCollections_WithAccessAll_Throws(
|
|
||||||
Organization organization,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser newUserData,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner, status: OrganizationUserStatusType.Confirmed)] OrganizationUser savingUser,
|
|
||||||
ICollection<CollectionAccessSelection> collections,
|
|
||||||
IEnumerable<Guid> groups,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
organization.FlexibleCollections = true;
|
|
||||||
newUserData.Id = oldUserData.Id;
|
|
||||||
newUserData.UserId = oldUserData.UserId;
|
|
||||||
newUserData.OrganizationId = oldUserData.OrganizationId = savingUser.OrganizationId = organization.Id;
|
|
||||||
newUserData.Permissions = CoreHelpers.ClassToJsonData(new Permissions());
|
|
||||||
newUserData.AccessAll = true;
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
|
||||||
.GetByIdAsync(organization.Id)
|
|
||||||
.Returns(organization);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>()
|
|
||||||
.ManageUsers(organization.Id)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetByIdAsync(oldUserData.Id)
|
|
||||||
.Returns(oldUserData);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser> { savingUser });
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveUserAsync(newUserData, oldUserData.UserId, collections, groups));
|
|
||||||
|
|
||||||
Assert.Contains("the accessall property has been deprecated", exception.Message.ToLowerInvariant());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task DeleteUser_InvalidUser(OrganizationUser organizationUser, OrganizationUser deletingUser,
|
public async Task DeleteUser_InvalidUser(OrganizationUser organizationUser, OrganizationUser deletingUser,
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
@ -2333,6 +1984,24 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup);
|
sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[OrganizationInviteCustomize(
|
||||||
|
InviteeUserType = OrganizationUserType.Custom,
|
||||||
|
InvitorUserType = OrganizationUserType.Custom
|
||||||
|
), BitAutoData]
|
||||||
|
public async Task ValidateOrganizationUserUpdatePermissions_WithCustomPermission_WhenSavingUserHasCustomPermission_Passes(
|
||||||
|
CurrentContextOrganization organization,
|
||||||
|
OrganizationUserInvite organizationUserInvite,
|
||||||
|
SutProvider<OrganizationService> sutProvider)
|
||||||
|
{
|
||||||
|
var invitePermissions = new Permissions { AccessReports = true };
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(organization.Id).Returns(true);
|
||||||
|
|
||||||
|
await sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organization.Id, organizationUserInvite.Type.Value, null, invitePermissions);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[OrganizationInviteCustomize(
|
[OrganizationInviteCustomize(
|
||||||
InviteeUserType = OrganizationUserType.Owner,
|
InviteeUserType = OrganizationUserType.Owner,
|
||||||
@ -2403,6 +2072,113 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
Assert.Contains("custom users can only grant the same custom permissions that they have.", exception.Message.ToLowerInvariant());
|
Assert.Contains("custom users can only grant the same custom permissions that they have.", exception.Message.ToLowerInvariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task HasConfirmedOwnersExceptAsync_WithConfirmedOwners_ReturnsTrue(
|
||||||
|
Guid organizationId,
|
||||||
|
IEnumerable<Guid> organizationUsersId,
|
||||||
|
ICollection<OrganizationUser> owners,
|
||||||
|
SutProvider<OrganizationService> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
||||||
|
.Returns(owners);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task HasConfirmedOwnersExceptAsync_WithConfirmedProviders_ReturnsTrue(
|
||||||
|
Guid organizationId,
|
||||||
|
IEnumerable<Guid> organizationUsersId,
|
||||||
|
ICollection<ProviderUser> providerUsers,
|
||||||
|
SutProvider<OrganizationService> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
||||||
|
.Returns(new List<OrganizationUser>());
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IProviderUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)
|
||||||
|
.Returns(providerUsers);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task HasConfirmedOwnersExceptAsync_WithNoConfirmedOwnersOrProviders_ReturnsFalse(
|
||||||
|
Guid organizationId,
|
||||||
|
IEnumerable<Guid> organizationUsersId,
|
||||||
|
SutProvider<OrganizationService> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
||||||
|
.Returns(new List<OrganizationUser>());
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IProviderUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)
|
||||||
|
.Returns(new List<ProviderUser>());
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
||||||
|
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(OrganizationUserType.Owner)]
|
||||||
|
[BitAutoData(OrganizationUserType.Admin)]
|
||||||
|
[BitAutoData(OrganizationUserType.User)]
|
||||||
|
[BitAutoData(OrganizationUserType.Manager)]
|
||||||
|
public async Task ValidateOrganizationCustomPermissionsEnabledAsync_WithNotCustomType_IsValid(
|
||||||
|
OrganizationUserType newType,
|
||||||
|
Guid organizationId,
|
||||||
|
SutProvider<OrganizationService> sutProvider)
|
||||||
|
{
|
||||||
|
await sutProvider.Sut.ValidateOrganizationCustomPermissionsEnabledAsync(organizationId, newType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task ValidateOrganizationCustomPermissionsEnabledAsync_NotExistingOrg_ThrowsNotFound(
|
||||||
|
Guid organizationId,
|
||||||
|
SutProvider<OrganizationService> sutProvider)
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.ValidateOrganizationCustomPermissionsEnabledAsync(organizationId, OrganizationUserType.Custom));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task ValidateOrganizationCustomPermissionsEnabledAsync_WithUseCustomPermissionsDisabled_ThrowsBadRequest(
|
||||||
|
Organization organization,
|
||||||
|
SutProvider<OrganizationService> sutProvider)
|
||||||
|
{
|
||||||
|
organization.UseCustomPermissions = false;
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(organization.Id)
|
||||||
|
.Returns(organization);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.ValidateOrganizationCustomPermissionsEnabledAsync(organization.Id, OrganizationUserType.Custom));
|
||||||
|
|
||||||
|
Assert.Contains("to enable custom permissions", exception.Message.ToLowerInvariant());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task ValidateOrganizationCustomPermissionsEnabledAsync_WithUseCustomPermissionsEnabled_IsValid(
|
||||||
|
Organization organization,
|
||||||
|
SutProvider<OrganizationService> sutProvider)
|
||||||
|
{
|
||||||
|
organization.UseCustomPermissions = true;
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(organization.Id)
|
||||||
|
.Returns(organization);
|
||||||
|
|
||||||
|
await sutProvider.Sut.ValidateOrganizationCustomPermissionsEnabledAsync(organization.Id, OrganizationUserType.Custom);
|
||||||
|
}
|
||||||
|
|
||||||
// Must set real guids in order for dictionary of guids to not throw aggregate exceptions
|
// Must set real guids in order for dictionary of guids to not throw aggregate exceptions
|
||||||
private void SetupOrgUserRepositoryCreateManyAsyncMock(IOrganizationUserRepository organizationUserRepository)
|
private void SetupOrgUserRepositoryCreateManyAsyncMock(IOrganizationUserRepository organizationUserRepository)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user