From 92716fe319265114f7ecf07fd6258fc0d6b82935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:42:30 +0100 Subject: [PATCH] [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 --- .../OrganizationUsersController.cs | 5 +- .../Public/Controllers/MembersController.cs | 5 +- .../IUpdateOrganizationUserCommand.cs | 10 + .../UpdateOrganizationUserCommand.cs | 111 ++++ .../Services/IOrganizationService.cs | 2 +- .../Implementations/OrganizationService.cs | 80 +-- ...OrganizationServiceCollectionExtensions.cs | 1 + .../OrganizationUsersControllerTests.cs | 17 +- .../UpdateOrganizationUserCommandTests.cs | 158 ++++++ .../Services/OrganizationServiceTests.cs | 474 +++++------------- 10 files changed, 424 insertions(+), 439 deletions(-) create mode 100644 src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Interfaces/IUpdateOrganizationUserCommand.cs create mode 100644 src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/UpdateOrganizationUserCommand.cs create mode 100644 test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/UpdateOrganizationUserCommandTests.cs diff --git a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index 9cc62490e9..36d266fdf4 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -38,6 +38,7 @@ public class OrganizationUsersController : Controller private readonly ICurrentContext _currentContext; private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery; private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; + private readonly IUpdateOrganizationUserCommand _updateOrganizationUserCommand; private readonly IUpdateOrganizationUserGroupsCommand _updateOrganizationUserGroupsCommand; private readonly IAcceptOrgUserCommand _acceptOrgUserCommand; private readonly IAuthorizationService _authorizationService; @@ -55,6 +56,7 @@ public class OrganizationUsersController : Controller ICurrentContext currentContext, ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery, IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand, + IUpdateOrganizationUserCommand updateOrganizationUserCommand, IUpdateOrganizationUserGroupsCommand updateOrganizationUserGroupsCommand, IAcceptOrgUserCommand acceptOrgUserCommand, IAuthorizationService authorizationService, @@ -71,6 +73,7 @@ public class OrganizationUsersController : Controller _currentContext = currentContext; _countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery; _updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand; + _updateOrganizationUserCommand = updateOrganizationUserCommand; _updateOrganizationUserGroupsCommand = updateOrganizationUserGroupsCommand; _acceptOrgUserCommand = acceptOrgUserCommand; _authorizationService = authorizationService; @@ -338,7 +341,7 @@ public class OrganizationUsersController : Controller ? null : model.Groups; - await _organizationService.SaveUserAsync(model.ToOrganizationUser(organizationUser), userId, + await _updateOrganizationUserCommand.UpdateUserAsync(model.ToOrganizationUser(organizationUser), userId, model.Collections?.Select(c => c.ToSelectionReadOnly()).ToList(), groups); } diff --git a/src/Api/AdminConsole/Public/Controllers/MembersController.cs b/src/Api/AdminConsole/Public/Controllers/MembersController.cs index 4c40022990..60aab4f2f9 100644 --- a/src/Api/AdminConsole/Public/Controllers/MembersController.cs +++ b/src/Api/AdminConsole/Public/Controllers/MembersController.cs @@ -21,6 +21,7 @@ public class MembersController : Controller private readonly IOrganizationService _organizationService; private readonly IUserService _userService; private readonly ICurrentContext _currentContext; + private readonly IUpdateOrganizationUserCommand _updateOrganizationUserCommand; private readonly IUpdateOrganizationUserGroupsCommand _updateOrganizationUserGroupsCommand; private readonly IApplicationCacheService _applicationCacheService; @@ -30,6 +31,7 @@ public class MembersController : Controller IOrganizationService organizationService, IUserService userService, ICurrentContext currentContext, + IUpdateOrganizationUserCommand updateOrganizationUserCommand, IUpdateOrganizationUserGroupsCommand updateOrganizationUserGroupsCommand, IApplicationCacheService applicationCacheService) { @@ -38,6 +40,7 @@ public class MembersController : Controller _organizationService = organizationService; _userService = userService; _currentContext = currentContext; + _updateOrganizationUserCommand = updateOrganizationUserCommand; _updateOrganizationUserGroupsCommand = updateOrganizationUserGroupsCommand; _applicationCacheService = applicationCacheService; } @@ -154,7 +157,7 @@ public class MembersController : Controller var updatedUser = model.ToOrganizationUser(existingUser); var flexibleCollectionsIsEnabled = await FlexibleCollectionsIsEnabledAsync(_currentContext.OrganizationId.Value); 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; if (existingUser.UserId.HasValue) { diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Interfaces/IUpdateOrganizationUserCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Interfaces/IUpdateOrganizationUserCommand.cs new file mode 100644 index 0000000000..254a97b75e --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Interfaces/IUpdateOrganizationUserCommand.cs @@ -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 collections, IEnumerable? groups); +} diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/UpdateOrganizationUserCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/UpdateOrganizationUserCommand.cs new file mode 100644 index 0000000000..9d25e6442c --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/UpdateOrganizationUserCommand.cs @@ -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 collections, IEnumerable? 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(); + } + await _organizationUserRepository.ReplaceAsync(user, collections); + + if (groups != null) + { + await _organizationUserRepository.UpdateGroupsAsync(user.Id, groups); + } + + await _eventService.LogOrganizationUserEventAsync(user, EventType.OrganizationUser_Updated); + } +} diff --git a/src/Core/AdminConsole/Services/IOrganizationService.cs b/src/Core/AdminConsole/Services/IOrganizationService.cs index 2c82518d8f..32e3ca0ef1 100644 --- a/src/Core/AdminConsole/Services/IOrganizationService.cs +++ b/src/Core/AdminConsole/Services/IOrganizationService.cs @@ -56,7 +56,6 @@ public interface IOrganizationService Guid confirmingUserId, IUserService userService); Task>> ConfirmUsersAsync(Guid organizationId, Dictionary keys, Guid confirmingUserId, IUserService userService); - Task SaveUserAsync(OrganizationUser user, Guid? savingUserId, ICollection collections, IEnumerable 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.")] @@ -93,4 +92,5 @@ public interface IOrganizationService void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade); Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType, OrganizationUserType? oldType, Permissions permissions); + Task ValidateOrganizationCustomPermissionsEnabledAsync(Guid organizationId, OrganizationUserType newType); } diff --git a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs index 35404f85a3..61b6a2c943 100644 --- a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs +++ b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs @@ -1467,84 +1467,6 @@ public class OrganizationService : IOrganizationService } } - public async Task SaveUserAsync(OrganizationUser user, Guid? savingUserId, - ICollection collections, - IEnumerable 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(); - } - 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.")] 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) { diff --git a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs index 3f303e3a90..3cc422decf 100644 --- a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs @@ -90,6 +90,7 @@ public static class OrganizationServiceCollectionExtensions private static void AddOrganizationUserCommands(this IServiceCollection services) { services.AddScoped(); + services.AddScoped(); services.AddScoped(); } diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs index 363d6db351..e64ca09f8a 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs @@ -5,6 +5,7 @@ using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; +using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Entities; @@ -121,12 +122,12 @@ public class OrganizationUsersControllerTests await sutProvider.Sut.Put(orgId, organizationUser.Id, model); - sutProvider.GetDependency().Received(1).SaveUserAsync(Arg.Is(ou => - ou.Type == model.Type && - ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) && - ou.AccessSecretsManager == model.AccessSecretsManager && - ou.Id == orgUserId && - ou.Email == orgUserEmail), + await sutProvider.GetDependency().Received(1).UpdateUserAsync(Arg.Is(ou => + ou.Type == model.Type && + ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) && + ou.AccessSecretsManager == model.AccessSecretsManager && + ou.Id == orgUserId && + ou.Email == orgUserEmail), savingUserId, Arg.Is>(cas => cas.All(c => model.Collections.Any(m => m.Id == c.Id))), @@ -157,7 +158,7 @@ public class OrganizationUsersControllerTests await sutProvider.Sut.Put(orgId, organizationUser.Id, model); - sutProvider.GetDependency().Received(1).SaveUserAsync(Arg.Is(ou => + await sutProvider.GetDependency().Received(1).UpdateUserAsync(Arg.Is(ou => ou.Type == model.Type && ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) && ou.AccessSecretsManager == model.AccessSecretsManager && @@ -193,7 +194,7 @@ public class OrganizationUsersControllerTests await sutProvider.Sut.Put(orgId, organizationUser.Id, model); - sutProvider.GetDependency().Received(1).SaveUserAsync(Arg.Is(ou => + await sutProvider.GetDependency().Received(1).UpdateUserAsync(Arg.Is(ou => ou.Type == model.Type && ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) && ou.AccessSecretsManager == model.AccessSecretsManager && diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/UpdateOrganizationUserCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/UpdateOrganizationUserCommandTests.cs new file mode 100644 index 0000000000..ed0a1cdffe --- /dev/null +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/UpdateOrganizationUserCommandTests.cs @@ -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 collections, IEnumerable groups, SutProvider sutProvider) + { + user.Id = default(Guid); + var exception = await Assert.ThrowsAsync( + () => 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 collections, + IEnumerable groups, + Permissions permissions, + [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser, + SutProvider sutProvider) + { + var organizationRepository = sutProvider.GetDependency(); + var organizationUserRepository = sutProvider.GetDependency(); + var organizationService = sutProvider.GetDependency(); + + 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 { savingUser }); + organizationService + .HasConfirmedOwnersExceptAsync( + newUserData.OrganizationId, + Arg.Is>(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()); + await organizationService.Received(1).ValidateOrganizationCustomPermissionsEnabledAsync( + newUserData.OrganizationId, + newUserData.Type); + await organizationService.Received(1).HasConfirmedOwnersExceptAsync( + newUserData.OrganizationId, + Arg.Is>(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 collections, + IEnumerable groups, + SutProvider 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() + .GetByIdAsync(organization.Id) + .Returns(organization); + + sutProvider.GetDependency() + .HasConfirmedOwnersExceptAsync(newUserData.OrganizationId, Arg.Is>(i => i.Contains(newUserData.Id))) + .Returns(true); + + sutProvider.GetDependency() + .GetByIdAsync(oldUserData.Id) + .Returns(oldUserData); + + sutProvider.GetDependency() + .GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner) + .Returns(new List { savingUser }); + + var exception = await Assert.ThrowsAsync( + () => 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 collections, + IEnumerable groups, + SutProvider 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() + .GetByIdAsync(organization.Id) + .Returns(organization); + + sutProvider.GetDependency() + .HasConfirmedOwnersExceptAsync( + newUserData.OrganizationId, + Arg.Is>(i => i.Contains(newUserData.Id))) + .Returns(true); + + sutProvider.GetDependency() + .GetByIdAsync(oldUserData.Id) + .Returns(oldUserData); + + sutProvider.GetDependency() + .GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner) + .Returns(new List { savingUser }); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpdateUserAsync(newUserData, oldUserData.UserId, collections, groups)); + + Assert.Contains("the accessall property has been deprecated", exception.Message.ToLowerInvariant()); + } +} diff --git a/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs b/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs index bd7c6d4c59..fa2090d37d 100644 --- a/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs @@ -1137,355 +1137,6 @@ OrganizationUserInvite invite, SutProvider sutProvider) sutProvider.GetDependency().ManageUsers(organization.Id).Returns(true); } - [Theory, BitAutoData] - public async Task SaveUser_NoUserId_Throws(OrganizationUser user, Guid? savingUserId, - ICollection collections, IEnumerable groups, SutProvider sutProvider) - { - user.Id = default(Guid); - var exception = await Assert.ThrowsAsync( - () => 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 collections, IEnumerable groups, SutProvider sutProvider) - { - var organizationUserRepository = sutProvider.GetDependency(); - organizationUserRepository.GetByIdAsync(user.Id).Returns(user); - var exception = await Assert.ThrowsAsync( - () => 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 collections, - IEnumerable groups, - Permissions permissions, - [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser, - SutProvider sutProvider) - { - var organizationRepository = sutProvider.GetDependency(); - var organizationUserRepository = sutProvider.GetDependency(); - var currentContext = sutProvider.GetDependency(); - - 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 { 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 collections, - IEnumerable groups, - [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser, - SutProvider sutProvider) - { - organization.UseCustomPermissions = false; - - var organizationRepository = sutProvider.GetDependency(); - var organizationUserRepository = sutProvider.GetDependency(); - var currentContext = sutProvider.GetDependency(); - - 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 { savingUser }); - currentContext.OrganizationOwner(savingUser.OrganizationId).Returns(true); - - var exception = await Assert.ThrowsAsync( - () => 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 collections, - IEnumerable groups, - Permissions permissions, - [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser, - SutProvider sutProvider) - { - organization.UseCustomPermissions = false; - - var organizationRepository = sutProvider.GetDependency(); - var organizationUserRepository = sutProvider.GetDependency(); - var currentContext = sutProvider.GetDependency(); - - 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 { 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 collections, - IEnumerable groups, - Permissions permissions, - [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser savingUser, - SutProvider sutProvider) - { - organization.UseCustomPermissions = true; - - var organizationRepository = sutProvider.GetDependency(); - var organizationUserRepository = sutProvider.GetDependency(); - var currentContext = sutProvider.GetDependency(); - - 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 { 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 collections, - IEnumerable groups, - [OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser savingUser, - [OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationOwner, - SutProvider sutProvider) - { - organization.UseCustomPermissions = true; - - var organizationRepository = sutProvider.GetDependency(); - var organizationUserRepository = sutProvider.GetDependency(); - var currentContext = sutProvider.GetDependency(); - - 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 { 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 collections, - IEnumerable groups, - [OrganizationUser(type: OrganizationUserType.Custom)] OrganizationUser savingUser, - SutProvider sutProvider) - { - organization.UseCustomPermissions = true; - - var organizationRepository = sutProvider.GetDependency(); - var organizationUserRepository = sutProvider.GetDependency(); - var currentContext = sutProvider.GetDependency(); - - 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( - () => 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 collections, - IEnumerable groups, - SutProvider sutProvider) - { - organization.UseCustomPermissions = true; - - var organizationRepository = sutProvider.GetDependency(); - var organizationUserRepository = sutProvider.GetDependency(); - var currentContext = sutProvider.GetDependency(); - - 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( - () => 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 collections, - IEnumerable groups, - SutProvider 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() - .GetByIdAsync(organization.Id) - .Returns(organization); - - sutProvider.GetDependency() - .ManageUsers(organization.Id) - .Returns(true); - - sutProvider.GetDependency() - .GetByIdAsync(oldUserData.Id) - .Returns(oldUserData); - - sutProvider.GetDependency() - .GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner) - .Returns(new List { savingUser }); - - var exception = await Assert.ThrowsAsync( - () => 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 collections, - IEnumerable groups, - SutProvider 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() - .GetByIdAsync(organization.Id) - .Returns(organization); - - sutProvider.GetDependency() - .ManageUsers(organization.Id) - .Returns(true); - - sutProvider.GetDependency() - .GetByIdAsync(oldUserData.Id) - .Returns(oldUserData); - - sutProvider.GetDependency() - .GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner) - .Returns(new List { savingUser }); - - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.SaveUserAsync(newUserData, oldUserData.UserId, collections, groups)); - - Assert.Contains("the accessall property has been deprecated", exception.Message.ToLowerInvariant()); - } - [Theory, BitAutoData] public async Task DeleteUser_InvalidUser(OrganizationUser organizationUser, OrganizationUser deletingUser, SutProvider sutProvider) @@ -2333,6 +1984,24 @@ OrganizationUserInvite invite, SutProvider sutProvider) 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 sutProvider) + { + var invitePermissions = new Permissions { AccessReports = true }; + sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().ManageUsers(organization.Id).Returns(true); + sutProvider.GetDependency().AccessReports(organization.Id).Returns(true); + + await sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organization.Id, organizationUserInvite.Type.Value, null, invitePermissions); + } + [Theory] [OrganizationInviteCustomize( InviteeUserType = OrganizationUserType.Owner, @@ -2403,6 +2072,113 @@ OrganizationUserInvite invite, SutProvider sutProvider) 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 organizationUsersId, + ICollection owners, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .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 organizationUsersId, + ICollection providerUsers, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner) + .Returns(new List()); + + sutProvider.GetDependency() + .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 organizationUsersId, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner) + .Returns(new List()); + + sutProvider.GetDependency() + .GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed) + .Returns(new List()); + + 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 sutProvider) + { + await sutProvider.Sut.ValidateOrganizationCustomPermissionsEnabledAsync(organizationId, newType); + } + + [Theory, BitAutoData] + public async Task ValidateOrganizationCustomPermissionsEnabledAsync_NotExistingOrg_ThrowsNotFound( + Guid organizationId, + SutProvider sutProvider) + { + await Assert.ThrowsAsync(() => sutProvider.Sut.ValidateOrganizationCustomPermissionsEnabledAsync(organizationId, OrganizationUserType.Custom)); + } + + [Theory, BitAutoData] + public async Task ValidateOrganizationCustomPermissionsEnabledAsync_WithUseCustomPermissionsDisabled_ThrowsBadRequest( + Organization organization, + SutProvider sutProvider) + { + organization.UseCustomPermissions = false; + + sutProvider.GetDependency() + .GetByIdAsync(organization.Id) + .Returns(organization); + + var exception = await Assert.ThrowsAsync( + () => 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 sutProvider) + { + organization.UseCustomPermissions = true; + + sutProvider.GetDependency() + .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 private void SetupOrgUserRepositoryCreateManyAsyncMock(IOrganizationUserRepository organizationUserRepository) {