From b83f95f78ccb475006e2118909a6d8cf30fb605c Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Thu, 25 Sep 2025 10:14:02 +1000 Subject: [PATCH] [PM-25097] Remove DeleteClaimedUserAccountRefactor flag (#6364) * Remove feature flag * Remove old code --- .../OrganizationUsersController.cs | 81 +-- .../CommandResult.cs | 2 +- ...eClaimedOrganizationUserAccountCommand.cs} | 12 +- ...ClaimedOrganizationUserAccountValidator.cs | 8 +- .../DeleteUserValidationRequest.cs | 2 +- .../Errors.cs | 2 +- ...eClaimedOrganizationUserAccountCommand.cs} | 4 +- ...laimedOrganizationUserAccountValidator.cs} | 4 +- .../ValidationResult.cs | 2 +- ...teClaimedOrganizationUserAccountCommand.cs | 239 -------- ...teClaimedOrganizationUserAccountCommand.cs | 19 - src/Core/Constants.cs | 1 - ...OrganizationServiceCollectionExtensions.cs | 8 +- .../OrganizationUserControllerTests.cs | 6 +- .../OrganizationUsersControllerTests.cs | 26 +- ...medOrganizationUserAccountCommandTests.cs} | 40 +- ...dOrganizationUserAccountValidatorTests.cs} | 34 +- ...imedOrganizationUserAccountCommandTests.cs | 526 ------------------ 18 files changed, 86 insertions(+), 930 deletions(-) rename src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/{DeleteClaimedAccountvNext => DeleteClaimedAccount}/CommandResult.cs (98%) rename src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/{DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountCommandvNext.cs => DeleteClaimedAccount/DeleteClaimedOrganizationUserAccountCommand.cs} (92%) rename src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/{DeleteClaimedAccountvNext => DeleteClaimedAccount}/DeleteClaimedOrganizationUserAccountValidator.cs (93%) rename src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/{DeleteClaimedAccountvNext => DeleteClaimedAccount}/DeleteUserValidationRequest.cs (92%) rename src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/{DeleteClaimedAccountvNext => DeleteClaimedAccount}/Errors.cs (97%) rename src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/{DeleteClaimedAccountvNext/IDeleteClaimedOrganizationUserAccountCommandvNext.cs => DeleteClaimedAccount/IDeleteClaimedOrganizationUserAccountCommand.cs} (87%) rename src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/{DeleteClaimedAccountvNext/IDeleteClaimedOrganizationUserAccountValidatorvNext.cs => DeleteClaimedAccount/IDeleteClaimedOrganizationUserAccountValidator.cs} (65%) rename src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/{DeleteClaimedAccountvNext => DeleteClaimedAccount}/ValidationResult.cs (97%) delete mode 100644 src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedOrganizationUserAccountCommand.cs delete mode 100644 src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Interfaces/IDeleteClaimedOrganizationUserAccountCommand.cs rename test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/{DeleteClaimedOrganizationUserAccountCommandvNextTests.cs => DeleteClaimedOrganizationUserAccountCommandTests.cs} (93%) rename test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/{DeleteClaimedOrganizationUserAccountValidatorvNextTests.cs => DeleteClaimedOrganizationUserAccountValidatorTests.cs} (97%) delete mode 100644 test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedOrganizationUserAccountCommandTests.cs diff --git a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index 16d6984334..74ac9b1255 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -11,7 +11,7 @@ using Bit.Api.Vault.AuthorizationHandlers.Collections; using Bit.Core; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; -using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.RestoreUser.v1; @@ -61,7 +61,6 @@ public class OrganizationUsersController : Controller private readonly IOrganizationUserUserDetailsQuery _organizationUserUserDetailsQuery; private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand; private readonly IDeleteClaimedOrganizationUserAccountCommand _deleteClaimedOrganizationUserAccountCommand; - private readonly IDeleteClaimedOrganizationUserAccountCommandvNext _deleteClaimedOrganizationUserAccountCommandvNext; private readonly IGetOrganizationUsersClaimedStatusQuery _getOrganizationUsersClaimedStatusQuery; private readonly IPolicyRequirementQuery _policyRequirementQuery; private readonly IFeatureService _featureService; @@ -90,7 +89,6 @@ public class OrganizationUsersController : Controller IOrganizationUserUserDetailsQuery organizationUserUserDetailsQuery, IRemoveOrganizationUserCommand removeOrganizationUserCommand, IDeleteClaimedOrganizationUserAccountCommand deleteClaimedOrganizationUserAccountCommand, - IDeleteClaimedOrganizationUserAccountCommandvNext deleteClaimedOrganizationUserAccountCommandvNext, IGetOrganizationUsersClaimedStatusQuery getOrganizationUsersClaimedStatusQuery, IPolicyRequirementQuery policyRequirementQuery, IFeatureService featureService, @@ -119,7 +117,6 @@ public class OrganizationUsersController : Controller _organizationUserUserDetailsQuery = organizationUserUserDetailsQuery; _removeOrganizationUserCommand = removeOrganizationUserCommand; _deleteClaimedOrganizationUserAccountCommand = deleteClaimedOrganizationUserAccountCommand; - _deleteClaimedOrganizationUserAccountCommandvNext = deleteClaimedOrganizationUserAccountCommandvNext; _getOrganizationUsersClaimedStatusQuery = getOrganizationUsersClaimedStatusQuery; _policyRequirementQuery = policyRequirementQuery; _featureService = featureService; @@ -539,21 +536,22 @@ public class OrganizationUsersController : Controller [HttpDelete("{id}/delete-account")] [Authorize] - public async Task DeleteAccount(Guid orgId, Guid id) + public async Task DeleteAccount(Guid orgId, Guid id) { - if (_featureService.IsEnabled(FeatureFlagKeys.DeleteClaimedUserAccountRefactor)) + var currentUserId = _userService.GetProperUserId(User); + if (currentUserId == null) { - await DeleteAccountvNext(orgId, id); - return; + return TypedResults.Unauthorized(); } - var currentUser = await _userService.GetUserByPrincipalAsync(User); - if (currentUser == null) - { - throw new UnauthorizedAccessException(); - } + var commandResult = await _deleteClaimedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUserId.Value); - await _deleteClaimedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUser.Id); + return commandResult.Result.Match( + error => error is NotFoundError + ? TypedResults.NotFound(new ErrorResponseModel(error.Message)) + : TypedResults.BadRequest(new ErrorResponseModel(error.Message)), + TypedResults.Ok + ); } [HttpPost("{id}/delete-account")] @@ -564,43 +562,24 @@ public class OrganizationUsersController : Controller await DeleteAccount(orgId, id); } - private async Task DeleteAccountvNext(Guid orgId, Guid id) - { - var currentUserId = _userService.GetProperUserId(User); - if (currentUserId == null) - { - return TypedResults.Unauthorized(); - } - - var commandResult = await _deleteClaimedOrganizationUserAccountCommandvNext.DeleteUserAsync(orgId, id, currentUserId.Value); - - return commandResult.Result.Match( - error => error is NotFoundError - ? TypedResults.NotFound(new ErrorResponseModel(error.Message)) - : TypedResults.BadRequest(new ErrorResponseModel(error.Message)), - TypedResults.Ok - ); - } - [HttpDelete("delete-account")] [Authorize] public async Task> BulkDeleteAccount(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model) { - if (_featureService.IsEnabled(FeatureFlagKeys.DeleteClaimedUserAccountRefactor)) - { - return await BulkDeleteAccountvNext(orgId, model); - } - - var currentUser = await _userService.GetUserByPrincipalAsync(User); - if (currentUser == null) + var currentUserId = _userService.GetProperUserId(User); + if (currentUserId == null) { throw new UnauthorizedAccessException(); } - var results = await _deleteClaimedOrganizationUserAccountCommand.DeleteManyUsersAsync(orgId, model.Ids, currentUser.Id); + var result = await _deleteClaimedOrganizationUserAccountCommand.DeleteManyUsersAsync(orgId, model.Ids, currentUserId.Value); - return new ListResponseModel(results.Select(r => - new OrganizationUserBulkResponseModel(r.OrganizationUserId, r.ErrorMessage))); + var responses = result.Select(r => r.Result.Match( + error => new OrganizationUserBulkResponseModel(r.Id, error.Message), + _ => new OrganizationUserBulkResponseModel(r.Id, string.Empty) + )); + + return new ListResponseModel(responses); } [HttpPost("delete-account")] @@ -611,24 +590,6 @@ public class OrganizationUsersController : Controller return await BulkDeleteAccount(orgId, model); } - private async Task> BulkDeleteAccountvNext(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model) - { - var currentUserId = _userService.GetProperUserId(User); - if (currentUserId == null) - { - throw new UnauthorizedAccessException(); - } - - var result = await _deleteClaimedOrganizationUserAccountCommandvNext.DeleteManyUsersAsync(orgId, model.Ids, currentUserId.Value); - - var responses = result.Select(r => r.Result.Match( - error => new OrganizationUserBulkResponseModel(r.Id, error.Message), - _ => new OrganizationUserBulkResponseModel(r.Id, string.Empty) - )); - - return new ListResponseModel(responses); - } - [HttpPut("{id}/revoke")] [Authorize] public async Task RevokeAsync(Guid orgId, Guid id) diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/CommandResult.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/CommandResult.cs similarity index 98% rename from src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/CommandResult.cs rename to src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/CommandResult.cs index 3dfbe4dbda..fbb00a908a 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/CommandResult.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/CommandResult.cs @@ -1,7 +1,7 @@ using OneOf; using OneOf.Types; -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; /// /// Represents the result of a command. diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountCommandvNext.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/DeleteClaimedOrganizationUserAccountCommand.cs similarity index 92% rename from src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountCommandvNext.cs rename to src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/DeleteClaimedOrganizationUserAccountCommand.cs index 3064a426fa..87c24c3ab4 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountCommandvNext.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/DeleteClaimedOrganizationUserAccountCommand.cs @@ -8,18 +8,18 @@ using Bit.Core.Services; using Microsoft.Extensions.Logging; using OneOf.Types; -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; -public class DeleteClaimedOrganizationUserAccountCommandvNext( +public class DeleteClaimedOrganizationUserAccountCommand( IUserService userService, IEventService eventService, IGetOrganizationUsersClaimedStatusQuery getOrganizationUsersClaimedStatusQuery, IOrganizationUserRepository organizationUserRepository, IUserRepository userRepository, IPushNotificationService pushService, - ILogger logger, - IDeleteClaimedOrganizationUserAccountValidatorvNext deleteClaimedOrganizationUserAccountValidatorvNext) - : IDeleteClaimedOrganizationUserAccountCommandvNext + ILogger logger, + IDeleteClaimedOrganizationUserAccountValidator deleteClaimedOrganizationUserAccountValidator) + : IDeleteClaimedOrganizationUserAccountCommand { public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid deletingUserId) { @@ -35,7 +35,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNext( var claimedStatuses = await getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(organizationId, orgUserIds); var internalRequests = CreateInternalRequests(organizationId, deletingUserId, orgUserIds, orgUsers, users, claimedStatuses); - var validationResults = (await deleteClaimedOrganizationUserAccountValidatorvNext.ValidateAsync(internalRequests)).ToList(); + var validationResults = (await deleteClaimedOrganizationUserAccountValidator.ValidateAsync(internalRequests)).ToList(); var validRequests = validationResults.ValidRequests(); await CancelPremiumsAsync(validRequests); diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountValidator.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/DeleteClaimedOrganizationUserAccountValidator.cs similarity index 93% rename from src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountValidator.cs rename to src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/DeleteClaimedOrganizationUserAccountValidator.cs index 7a88841d2f..315d45ea69 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountValidator.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/DeleteClaimedOrganizationUserAccountValidator.cs @@ -2,14 +2,14 @@ using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Repositories; -using static Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext.ValidationResultHelpers; +using static Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount.ValidationResultHelpers; -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; -public class DeleteClaimedOrganizationUserAccountValidatorvNext( +public class DeleteClaimedOrganizationUserAccountValidator( ICurrentContext currentContext, IOrganizationUserRepository organizationUserRepository, - IProviderUserRepository providerUserRepository) : IDeleteClaimedOrganizationUserAccountValidatorvNext + IProviderUserRepository providerUserRepository) : IDeleteClaimedOrganizationUserAccountValidator { public async Task>> ValidateAsync(IEnumerable requests) { diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteUserValidationRequest.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/DeleteUserValidationRequest.cs similarity index 92% rename from src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteUserValidationRequest.cs rename to src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/DeleteUserValidationRequest.cs index 5fd95dc73c..067d7ce04c 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteUserValidationRequest.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/DeleteUserValidationRequest.cs @@ -1,6 +1,6 @@ using Bit.Core.Entities; -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; public class DeleteUserValidationRequest { diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/Errors.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/Errors.cs similarity index 97% rename from src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/Errors.cs rename to src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/Errors.cs index d991a882b8..6c8f7ee00c 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/Errors.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/Errors.cs @@ -1,4 +1,4 @@ -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; /// /// A strongly typed error containing a reason that an action failed. diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/IDeleteClaimedOrganizationUserAccountCommandvNext.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/IDeleteClaimedOrganizationUserAccountCommand.cs similarity index 87% rename from src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/IDeleteClaimedOrganizationUserAccountCommandvNext.cs rename to src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/IDeleteClaimedOrganizationUserAccountCommand.cs index 2c462a2acf..983a3a4f21 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/IDeleteClaimedOrganizationUserAccountCommandvNext.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/IDeleteClaimedOrganizationUserAccountCommand.cs @@ -1,6 +1,6 @@ -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; -public interface IDeleteClaimedOrganizationUserAccountCommandvNext +public interface IDeleteClaimedOrganizationUserAccountCommand { /// /// Removes a user from an organization and deletes all of their associated user data. diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/IDeleteClaimedOrganizationUserAccountValidatorvNext.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/IDeleteClaimedOrganizationUserAccountValidator.cs similarity index 65% rename from src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/IDeleteClaimedOrganizationUserAccountValidatorvNext.cs rename to src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/IDeleteClaimedOrganizationUserAccountValidator.cs index f6125a0355..f1a2c71b1b 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/IDeleteClaimedOrganizationUserAccountValidatorvNext.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/IDeleteClaimedOrganizationUserAccountValidator.cs @@ -1,6 +1,6 @@ -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; -public interface IDeleteClaimedOrganizationUserAccountValidatorvNext +public interface IDeleteClaimedOrganizationUserAccountValidator { Task>> ValidateAsync(IEnumerable requests); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/ValidationResult.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/ValidationResult.cs similarity index 97% rename from src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/ValidationResult.cs rename to src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/ValidationResult.cs index 23d2fbb7ce..c84a0aeda1 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/ValidationResult.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccount/ValidationResult.cs @@ -1,7 +1,7 @@ using OneOf; using OneOf.Types; -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; /// /// Represents the result of validating a request. diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedOrganizationUserAccountCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedOrganizationUserAccountCommand.cs deleted file mode 100644 index 60a1c8bfbf..0000000000 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedOrganizationUserAccountCommand.cs +++ /dev/null @@ -1,239 +0,0 @@ -using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; -using Bit.Core.AdminConsole.Repositories; -using Bit.Core.Context; -using Bit.Core.Entities; -using Bit.Core.Enums; -using Bit.Core.Exceptions; -using Bit.Core.Platform.Push; -using Bit.Core.Repositories; -using Bit.Core.Services; - -#nullable enable - -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers; - -public class DeleteClaimedOrganizationUserAccountCommand : IDeleteClaimedOrganizationUserAccountCommand -{ - private readonly IUserService _userService; - private readonly IEventService _eventService; - private readonly IGetOrganizationUsersClaimedStatusQuery _getOrganizationUsersClaimedStatusQuery; - private readonly IOrganizationUserRepository _organizationUserRepository; - private readonly IUserRepository _userRepository; - private readonly ICurrentContext _currentContext; - private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery; - private readonly IPushNotificationService _pushService; - private readonly IOrganizationRepository _organizationRepository; - private readonly IProviderUserRepository _providerUserRepository; - public DeleteClaimedOrganizationUserAccountCommand( - IUserService userService, - IEventService eventService, - IGetOrganizationUsersClaimedStatusQuery getOrganizationUsersClaimedStatusQuery, - IOrganizationUserRepository organizationUserRepository, - IUserRepository userRepository, - ICurrentContext currentContext, - IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery, - IPushNotificationService pushService, - IOrganizationRepository organizationRepository, - IProviderUserRepository providerUserRepository) - { - _userService = userService; - _eventService = eventService; - _getOrganizationUsersClaimedStatusQuery = getOrganizationUsersClaimedStatusQuery; - _organizationUserRepository = organizationUserRepository; - _userRepository = userRepository; - _currentContext = currentContext; - _hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery; - _pushService = pushService; - _organizationRepository = organizationRepository; - _providerUserRepository = providerUserRepository; - } - - public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId) - { - var organizationUser = await _organizationUserRepository.GetByIdAsync(organizationUserId); - if (organizationUser == null || organizationUser.OrganizationId != organizationId) - { - throw new NotFoundException("Member not found."); - } - - var claimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(organizationId, new[] { organizationUserId }); - var hasOtherConfirmedOwners = await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, new[] { organizationUserId }, includeProvider: true); - - await ValidateDeleteUserAsync(organizationId, organizationUser, deletingUserId, claimedStatus, hasOtherConfirmedOwners); - - var user = await _userRepository.GetByIdAsync(organizationUser.UserId!.Value); - if (user == null) - { - throw new NotFoundException("Member not found."); - } - - await _userService.DeleteAsync(user); - await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Deleted); - } - - public async Task> DeleteManyUsersAsync(Guid organizationId, IEnumerable orgUserIds, Guid? deletingUserId) - { - var orgUsers = await _organizationUserRepository.GetManyAsync(orgUserIds); - var userIds = orgUsers.Where(ou => ou.UserId.HasValue).Select(ou => ou.UserId!.Value).ToList(); - var users = await _userRepository.GetManyAsync(userIds); - - var claimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(organizationId, orgUserIds); - var hasOtherConfirmedOwners = await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, orgUserIds, includeProvider: true); - - var results = new List<(Guid OrganizationUserId, string? ErrorMessage)>(); - foreach (var orgUserId in orgUserIds) - { - try - { - var orgUser = orgUsers.FirstOrDefault(ou => ou.Id == orgUserId); - if (orgUser == null || orgUser.OrganizationId != organizationId) - { - throw new NotFoundException("Member not found."); - } - - await ValidateDeleteUserAsync(organizationId, orgUser, deletingUserId, claimedStatus, hasOtherConfirmedOwners); - - var user = users.FirstOrDefault(u => u.Id == orgUser.UserId); - if (user == null) - { - throw new NotFoundException("Member not found."); - } - - await ValidateUserMembershipAndPremiumAsync(user); - - results.Add((orgUserId, string.Empty)); - } - catch (Exception ex) - { - results.Add((orgUserId, ex.Message)); - } - } - - var orgUserResultsToDelete = results.Where(result => string.IsNullOrEmpty(result.ErrorMessage)); - var orgUsersToDelete = orgUsers.Where(orgUser => orgUserResultsToDelete.Any(result => orgUser.Id == result.OrganizationUserId)); - var usersToDelete = users.Where(user => orgUsersToDelete.Any(orgUser => orgUser.UserId == user.Id)); - - if (usersToDelete.Any()) - { - await DeleteManyAsync(usersToDelete); - } - - await LogDeletedOrganizationUsersAsync(orgUsers, results); - - return results; - } - - private async Task ValidateDeleteUserAsync(Guid organizationId, OrganizationUser orgUser, Guid? deletingUserId, IDictionary claimedStatus, bool hasOtherConfirmedOwners) - { - if (!orgUser.UserId.HasValue || orgUser.Status == OrganizationUserStatusType.Invited) - { - throw new BadRequestException("You cannot delete a member with Invited status."); - } - - if (deletingUserId.HasValue && orgUser.UserId.Value == deletingUserId.Value) - { - throw new BadRequestException("You cannot delete yourself."); - } - - if (orgUser.Type == OrganizationUserType.Owner) - { - if (deletingUserId.HasValue && !await _currentContext.OrganizationOwner(organizationId)) - { - throw new BadRequestException("Only owners can delete other owners."); - } - - if (!hasOtherConfirmedOwners) - { - throw new BadRequestException("Organization must have at least one confirmed owner."); - } - } - - if (orgUser.Type == OrganizationUserType.Admin && await _currentContext.OrganizationCustom(organizationId)) - { - throw new BadRequestException("Custom users can not delete admins."); - } - - if (!claimedStatus.TryGetValue(orgUser.Id, out var isClaimed) || !isClaimed) - { - throw new BadRequestException("Member is not claimed by the organization."); - } - } - - private async Task LogDeletedOrganizationUsersAsync( - IEnumerable orgUsers, - IEnumerable<(Guid OrgUserId, string? ErrorMessage)> results) - { - var eventDate = DateTime.UtcNow; - var events = new List<(OrganizationUser OrgUser, EventType Event, DateTime? EventDate)>(); - - foreach (var (orgUserId, errorMessage) in results) - { - var orgUser = orgUsers.FirstOrDefault(ou => ou.Id == orgUserId); - // If the user was not found or there was an error, we skip logging the event - if (orgUser == null || !string.IsNullOrEmpty(errorMessage)) - { - continue; - } - - events.Add((orgUser, EventType.OrganizationUser_Deleted, eventDate)); - } - - if (events.Any()) - { - await _eventService.LogOrganizationUserEventsAsync(events); - } - } - private async Task DeleteManyAsync(IEnumerable users) - { - - await _userRepository.DeleteManyAsync(users); - foreach (var user in users) - { - await _pushService.PushLogOutAsync(user.Id); - } - - } - - private async Task ValidateUserMembershipAndPremiumAsync(User user) - { - // Check if user is the only owner of any organizations. - var onlyOwnerCount = await _organizationUserRepository.GetCountByOnlyOwnerAsync(user.Id); - if (onlyOwnerCount > 0) - { - throw new BadRequestException("Cannot delete this user because it is the sole owner of at least one organization. Please delete these organizations or upgrade another user."); - } - - var orgs = await _organizationUserRepository.GetManyDetailsByUserAsync(user.Id, OrganizationUserStatusType.Confirmed); - if (orgs.Count == 1) - { - var org = await _organizationRepository.GetByIdAsync(orgs.First().OrganizationId); - if (org != null && (!org.Enabled || string.IsNullOrWhiteSpace(org.GatewaySubscriptionId))) - { - var orgCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(org.Id); - if (orgCount <= 1) - { - await _organizationRepository.DeleteAsync(org); - } - else - { - throw new BadRequestException("Cannot delete this user because it is the sole owner of at least one organization. Please delete these organizations or upgrade another user."); - } - } - } - - var onlyOwnerProviderCount = await _providerUserRepository.GetCountByOnlyOwnerAsync(user.Id); - if (onlyOwnerProviderCount > 0) - { - throw new BadRequestException("Cannot delete this user because it is the sole owner of at least one provider. Please delete these providers or upgrade another user."); - } - - if (!string.IsNullOrWhiteSpace(user.GatewaySubscriptionId)) - { - try - { - await _userService.CancelPremiumAsync(user); - } - catch (GatewayException) { } - } - } -} diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Interfaces/IDeleteClaimedOrganizationUserAccountCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Interfaces/IDeleteClaimedOrganizationUserAccountCommand.cs deleted file mode 100644 index 1c79687be9..0000000000 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Interfaces/IDeleteClaimedOrganizationUserAccountCommand.cs +++ /dev/null @@ -1,19 +0,0 @@ -#nullable enable - -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; - -public interface IDeleteClaimedOrganizationUserAccountCommand -{ - /// - /// Removes a user from an organization and deletes all of their associated user data. - /// - Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId); - - /// - /// Removes multiple users from an organization and deletes all of their associated user data. - /// - /// - /// An error message for each user that could not be removed, otherwise null. - /// - Task> DeleteManyUsersAsync(Guid organizationId, IEnumerable orgUserIds, Guid? deletingUserId); -} diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 5c00db9da4..97c1399719 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -134,7 +134,6 @@ public static class FeatureFlagKeys public const string PM23845_VNextApplicationCache = "pm-24957-refactor-memory-application-cache"; public const string CipherRepositoryBulkResourceCreation = "pm-24951-cipher-repository-bulk-resource-creation-service"; public const string CollectionVaultRefactor = "pm-25030-resolve-ts-upgrade-errors"; - public const string DeleteClaimedUserAccountRefactor = "pm-25094-refactor-delete-managed-organization-user-command"; /* Auth Team */ public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence"; diff --git a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs index 1c38a27d1e..da05bc929c 100644 --- a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs @@ -13,7 +13,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Organizations; using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization; -using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation; @@ -132,12 +132,10 @@ public static class OrganizationServiceCollectionExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); - // vNext implementations (feature flagged) - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } private static void AddOrganizationApiKeyCommandsQueries(this IServiceCollection services) diff --git a/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUserControllerTests.cs b/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUserControllerTests.cs index b7839467e8..7c61a88bd8 100644 --- a/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUserControllerTests.cs +++ b/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUserControllerTests.cs @@ -7,7 +7,7 @@ using Bit.Api.Models.Request; using Bit.Api.Models.Response; using Bit.Core; using Bit.Core.AdminConsole.Entities; -using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Billing.Enums; using Bit.Core.Entities; @@ -33,10 +33,6 @@ public class OrganizationUserControllerTests : IClassFixture sutProvider) - { - sutProvider.GetDependency().ManageUsers(orgId).Returns(true); - sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(currentUser); - - await sutProvider.Sut.DeleteAccount(orgId, id); - - await sutProvider.GetDependency() - .Received(1) - .DeleteUserAsync(orgId, id, currentUser.Id); - } - - [Theory] - [BitAutoData] - public async Task DeleteAccount_WhenCurrentUserNotFound_ThrowsUnauthorizedAccessException( + public async Task DeleteAccount_WhenCurrentUserNotFound_ReturnsUnauthorizedResult( Guid orgId, Guid id, SutProvider sutProvider) { - sutProvider.GetDependency().ManageUsers(orgId).Returns(true); - sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs((User)null); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs((Guid?)null); - await Assert.ThrowsAsync(() => - sutProvider.Sut.DeleteAccount(orgId, id)); + var result = await sutProvider.Sut.DeleteAccount(orgId, id); + + Assert.IsType(result); } [Theory] diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountCommandvNextTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountCommandTests.cs similarity index 93% rename from test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountCommandvNextTests.cs rename to test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountCommandTests.cs index 679c1914c6..c223520a04 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountCommandvNextTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountCommandTests.cs @@ -1,4 +1,4 @@ -using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Entities; using Bit.Core.Enums; @@ -17,12 +17,12 @@ using Xunit; namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; [SutProviderCustomize] -public class DeleteClaimedOrganizationUserAccountCommandvNextTests +public class DeleteClaimedOrganizationUserAccountCommandTests { [Theory] [BitAutoData] public async Task DeleteUserAsync_WithValidSingleUser_CallsDeleteManyUsersAsync( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -65,7 +65,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests [Theory] [BitAutoData] public async Task DeleteManyUsersAsync_WithEmptyUserIds_ReturnsEmptyResults( - SutProvider sutProvider, + SutProvider sutProvider, Guid organizationId, Guid deletingUserId) { @@ -77,7 +77,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests [Theory] [BitAutoData] public async Task DeleteManyUsersAsync_WithValidUsers_DeletesUsersAndLogsEvents( - SutProvider sutProvider, + SutProvider sutProvider, User user1, User user2, Guid organizationId, @@ -135,7 +135,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests [Theory] [BitAutoData] public async Task DeleteManyUsersAsync_WithValidationErrors_ReturnsErrorResults( - SutProvider sutProvider, + SutProvider sutProvider, Guid organizationId, Guid orgUserId1, Guid orgUserId2, @@ -183,7 +183,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests [Theory] [BitAutoData] public async Task DeleteManyUsersAsync_WithMixedValidationResults_HandlesPartialSuccessCorrectly( - SutProvider sutProvider, + SutProvider sutProvider, User validUser, Guid organizationId, Guid validOrgUserId, @@ -243,7 +243,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests [Theory] [BitAutoData] public async Task DeleteManyUsersAsync_CancelPremiumsAsync_HandlesGatewayExceptionAndLogsWarning( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -285,7 +285,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests await sutProvider.GetDependency().Received(1).CancelPremiumAsync(user); await AssertSuccessfulUserOperations(sutProvider, [user], [orgUser]); - sutProvider.GetDependency>() + sutProvider.GetDependency>() .Received(1) .Log( LogLevel.Warning, @@ -299,7 +299,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests [Theory] [BitAutoData] public async Task CreateInternalRequests_CreatesCorrectRequestsForAllUsers( - SutProvider sutProvider, + SutProvider sutProvider, User user1, User user2, Guid organizationId, @@ -326,7 +326,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests .GetUsersOrganizationClaimedStatusAsync(organizationId, Arg.Any>()) .Returns(claimedStatuses); - sutProvider.GetDependency() + sutProvider.GetDependency() .ValidateAsync(Arg.Any>()) .Returns(callInfo => { @@ -338,7 +338,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests await sutProvider.Sut.DeleteManyUsersAsync(organizationId, orgUserIds, deletingUserId); // Assert - await sutProvider.GetDependency() + await sutProvider.GetDependency() .Received(1) .ValidateAsync(Arg.Is>(requests => requests.Count() == 2 && @@ -359,7 +359,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests [Theory] [BitAutoData] public async Task GetUsersAsync_WithNullUserIds_ReturnsEmptyCollection( - SutProvider sutProvider, + SutProvider sutProvider, Guid organizationId, Guid deletingUserId, [OrganizationUser] OrganizationUser orgUserWithoutUserId) @@ -374,7 +374,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests .GetManyAsync(Arg.Is>(ids => !ids.Any())) .Returns([]); - sutProvider.GetDependency() + sutProvider.GetDependency() .ValidateAsync(Arg.Any>()) .Returns(callInfo => { @@ -386,7 +386,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests await sutProvider.Sut.DeleteManyUsersAsync(organizationId, [orgUserWithoutUserId.Id], deletingUserId); // Assert - await sutProvider.GetDependency() + await sutProvider.GetDependency() .Received(1) .ValidateAsync(Arg.Is>(requests => requests.Count() == 1 && @@ -406,7 +406,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests ValidationResultHelpers.Invalid(request, error); private static void SetupRepositoryMocks( - SutProvider sutProvider, + SutProvider sutProvider, ICollection orgUsers, IEnumerable users, Guid organizationId, @@ -426,16 +426,16 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests } private static void SetupValidatorMock( - SutProvider sutProvider, + SutProvider sutProvider, IEnumerable> validationResults) { - sutProvider.GetDependency() + sutProvider.GetDependency() .ValidateAsync(Arg.Any>()) .Returns(validationResults); } private static async Task AssertSuccessfulUserOperations( - SutProvider sutProvider, + SutProvider sutProvider, IEnumerable expectedUsers, IEnumerable expectedOrgUsers) { @@ -457,7 +457,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests events.Any(e => e.Item1.Id == expectedOrgUser.Id && e.Item2 == EventType.OrganizationUser_Deleted)))); } - private static async Task AssertNoUserOperations(SutProvider sutProvider) + private static async Task AssertNoUserOperations(SutProvider sutProvider) { await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().DeleteManyAsync(default); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().PushLogOutAsync(default); diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountValidatorvNextTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountValidatorTests.cs similarity index 97% rename from test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountValidatorvNextTests.cs rename to test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountValidatorTests.cs index e51df6a626..30cc574ead 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountValidatorvNextTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedAccountvNext/DeleteClaimedOrganizationUserAccountValidatorTests.cs @@ -1,4 +1,4 @@ -using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; +using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Entities; @@ -13,12 +13,12 @@ using Xunit; namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext; [SutProviderCustomize] -public class DeleteClaimedOrganizationUserAccountValidatorvNextTests +public class DeleteClaimedOrganizationUserAccountValidatorTests { [Theory] [BitAutoData] public async Task ValidateAsync_WithValidSingleRequest_ReturnsValidResult( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -50,7 +50,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_WithMultipleValidRequests_ReturnsAllValidResults( - SutProvider sutProvider, + SutProvider sutProvider, User user1, User user2, Guid organizationId, @@ -97,7 +97,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_WithNullUser_ReturnsUserNotFoundError( - SutProvider sutProvider, + SutProvider sutProvider, Guid organizationId, Guid deletingUserId, [OrganizationUser] OrganizationUser organizationUser) @@ -123,7 +123,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_WithNullOrganizationUser_ReturnsUserNotFoundError( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId) @@ -149,7 +149,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_WithInvitedUser_ReturnsInvalidUserStatusError( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -178,7 +178,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_WhenDeletingYourself_ReturnsCannotDeleteYourselfError( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, [OrganizationUser] OrganizationUser organizationUser) @@ -206,7 +206,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_WithUnclaimedUser_ReturnsUserNotClaimedError( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -235,7 +235,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_DeletingOwnerWhenCurrentUserIsNotOwner_ReturnsCannotDeleteOwnersError( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -266,7 +266,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_DeletingOwnerWhenCurrentUserIsOwner_ReturnsValidResult( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -296,7 +296,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_WithSoleOwnerOfOrganization_ReturnsSoleOwnerError( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -331,7 +331,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_WithSoleProviderOwner_ReturnsSoleProviderError( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -366,7 +366,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_CustomUserDeletingAdmin_ReturnsCannotDeleteAdminsError( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -397,7 +397,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_AdminDeletingAdmin_ReturnsValidResult( - SutProvider sutProvider, + SutProvider sutProvider, User user, Guid organizationId, Guid deletingUserId, @@ -427,7 +427,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests [Theory] [BitAutoData] public async Task ValidateAsync_WithMixedValidAndInvalidRequests_ReturnsCorrespondingResults( - SutProvider sutProvider, + SutProvider sutProvider, User validUser, User invalidUser, Guid organizationId, @@ -475,7 +475,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests } private static void SetupMocks( - SutProvider sutProvider, + SutProvider sutProvider, Guid organizationId, Guid userId, OrganizationUserType currentUserType = OrganizationUserType.Owner) diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedOrganizationUserAccountCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedOrganizationUserAccountCommandTests.cs deleted file mode 100644 index 7f1b101d7a..0000000000 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/DeleteClaimedOrganizationUserAccountCommandTests.cs +++ /dev/null @@ -1,526 +0,0 @@ -using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers; -using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; -using Bit.Core.Context; -using Bit.Core.Entities; -using Bit.Core.Enums; -using Bit.Core.Exceptions; -using Bit.Core.Repositories; -using Bit.Core.Services; -using Bit.Core.Test.AutoFixture.OrganizationUserFixtures; -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 DeleteClaimedOrganizationUserAccountCommandTests -{ - [Theory] - [BitAutoData] - public async Task DeleteUserAsync_WithValidUser_DeletesUserAndLogsEvent( - SutProvider sutProvider, User user, Guid deletingUserId, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser) - { - // Arrange - organizationUser.UserId = user.Id; - - sutProvider.GetDependency() - .GetByIdAsync(user.Id) - .Returns(user); - - sutProvider.GetDependency() - .GetByIdAsync(organizationUser.Id) - .Returns(organizationUser); - - sutProvider.GetDependency() - .GetUsersOrganizationClaimedStatusAsync( - organizationUser.OrganizationId, - Arg.Is>(ids => ids.Contains(organizationUser.Id))) - .Returns(new Dictionary { { organizationUser.Id, true } }); - - sutProvider.GetDependency() - .HasConfirmedOwnersExceptAsync( - organizationUser.OrganizationId, - Arg.Is>(ids => ids.Contains(organizationUser.Id)), - includeProvider: Arg.Any()) - .Returns(true); - - // Act - await sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId); - - // Assert - await sutProvider.GetDependency().Received(1).DeleteAsync(user); - await sutProvider.GetDependency().Received(1) - .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Deleted); - } - - [Theory] - [BitAutoData] - public async Task DeleteUserAsync_WithUserNotFound_ThrowsException( - SutProvider sutProvider, - Guid organizationId, Guid organizationUserId) - { - // Arrange - sutProvider.GetDependency() - .GetByIdAsync(organizationUserId) - .Returns((OrganizationUser?)null); - - // Act - var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.DeleteUserAsync(organizationId, organizationUserId, null)); - - // Assert - Assert.Equal("Member not found.", exception.Message); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); - } - - [Theory] - [BitAutoData] - public async Task DeleteUserAsync_DeletingYourself_ThrowsException( - SutProvider sutProvider, - User user, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser, - Guid deletingUserId) - { - // Arrange - organizationUser.UserId = user.Id = deletingUserId; - - sutProvider.GetDependency() - .GetByIdAsync(organizationUser.Id) - .Returns(organizationUser); - - sutProvider.GetDependency().GetByIdAsync(user.Id) - .Returns(user); - - // Act - var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId)); - - // Assert - Assert.Equal("You cannot delete yourself.", exception.Message); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); - } - - [Theory] - [BitAutoData] - public async Task DeleteUserAsync_WhenUserIsInvited_ThrowsException( - SutProvider sutProvider, - [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser organizationUser) - { - // Arrange - organizationUser.UserId = null; - - sutProvider.GetDependency() - .GetByIdAsync(organizationUser.Id) - .Returns(organizationUser); - - // Act - var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, null)); - - // Assert - Assert.Equal("You cannot delete a member with Invited status.", exception.Message); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); - } - - [Theory] - [BitAutoData] - public async Task DeleteUserAsync_WhenCustomUserDeletesAdmin_ThrowsException( - SutProvider sutProvider, User user, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Admin)] OrganizationUser organizationUser, - Guid deletingUserId) - { - // Arrange - organizationUser.UserId = user.Id; - - sutProvider.GetDependency() - .GetByIdAsync(organizationUser.Id) - .Returns(organizationUser); - - sutProvider.GetDependency().GetByIdAsync(user.Id) - .Returns(user); - - sutProvider.GetDependency() - .OrganizationCustom(organizationUser.OrganizationId) - .Returns(true); - - // Act - var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId)); - - // Assert - Assert.Equal("Custom users can not delete admins.", exception.Message); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); - } - - [Theory] - [BitAutoData] - public async Task DeleteUserAsync_DeletingOwnerWhenNotOwner_ThrowsException( - SutProvider sutProvider, User user, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser organizationUser, - Guid deletingUserId) - { - // Arrange - organizationUser.UserId = user.Id; - - sutProvider.GetDependency() - .GetByIdAsync(organizationUser.Id) - .Returns(organizationUser); - - sutProvider.GetDependency().GetByIdAsync(user.Id) - .Returns(user); - - sutProvider.GetDependency() - .OrganizationOwner(organizationUser.OrganizationId) - .Returns(false); - - // Act - var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId)); - - // Assert - Assert.Equal("Only owners can delete other owners.", exception.Message); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); - } - - [Theory] - [BitAutoData] - public async Task DeleteUserAsync_DeletingLastConfirmedOwner_ThrowsException( - SutProvider sutProvider, User user, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser organizationUser, - Guid deletingUserId) - { - // Arrange - organizationUser.UserId = user.Id; - - sutProvider.GetDependency() - .GetByIdAsync(organizationUser.Id) - .Returns(organizationUser); - - sutProvider.GetDependency().GetByIdAsync(user.Id) - .Returns(user); - - sutProvider.GetDependency() - .OrganizationOwner(organizationUser.OrganizationId) - .Returns(true); - - sutProvider.GetDependency() - .HasConfirmedOwnersExceptAsync( - organizationUser.OrganizationId, - Arg.Is>(ids => ids.Contains(organizationUser.Id)), - includeProvider: Arg.Any()) - .Returns(false); - - // Act - var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId)); - - // Assert - Assert.Equal("Organization must have at least one confirmed owner.", exception.Message); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); - } - - [Theory] - [BitAutoData] - public async Task DeleteUserAsync_WithUserNotManaged_ThrowsException( - SutProvider sutProvider, User user, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser) - { - // Arrange - organizationUser.UserId = user.Id; - - sutProvider.GetDependency() - .GetByIdAsync(organizationUser.Id) - .Returns(organizationUser); - - sutProvider.GetDependency().GetByIdAsync(user.Id) - .Returns(user); - - sutProvider.GetDependency() - .GetUsersOrganizationClaimedStatusAsync(organizationUser.OrganizationId, Arg.Any>()) - .Returns(new Dictionary { { organizationUser.Id, false } }); - - // Act - var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, null)); - - // Assert - Assert.Equal("Member is not claimed by the organization.", exception.Message); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); - } - - [Theory] - [BitAutoData] - public async Task DeleteManyUsersAsync_WithValidUsers_DeletesUsersAndLogsEvents( - SutProvider sutProvider, User user1, User user2, Guid organizationId, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser1, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser2) - { - // Arrange - orgUser1.OrganizationId = orgUser2.OrganizationId = organizationId; - orgUser1.UserId = user1.Id; - orgUser2.UserId = user2.Id; - - sutProvider.GetDependency() - .GetManyAsync(Arg.Any>()) - .Returns(new List { orgUser1, orgUser2 }); - - sutProvider.GetDependency() - .GetManyAsync(Arg.Is>(ids => ids.Contains(user1.Id) && ids.Contains(user2.Id))) - .Returns(new[] { user1, user2 }); - - sutProvider.GetDependency() - .GetUsersOrganizationClaimedStatusAsync(organizationId, Arg.Any>()) - .Returns(new Dictionary { { orgUser1.Id, true }, { orgUser2.Id, true } }); - - // Act - var userIds = new[] { orgUser1.Id, orgUser2.Id }; - var results = await sutProvider.Sut.DeleteManyUsersAsync(organizationId, userIds, null); - - // Assert - Assert.Equal(2, results.Count()); - Assert.All(results, r => Assert.Empty(r.Item2)); - - await sutProvider.GetDependency().Received(1).GetManyAsync(userIds); - await sutProvider.GetDependency().Received(1).DeleteManyAsync(Arg.Is>(users => users.Any(u => u.Id == user1.Id) && users.Any(u => u.Id == user2.Id))); - await sutProvider.GetDependency().Received(1).LogOrganizationUserEventsAsync( - Arg.Is>(events => - events.Count(e => e.Item1.Id == orgUser1.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1 - && events.Count(e => e.Item1.Id == orgUser2.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1)); - } - - [Theory] - [BitAutoData] - public async Task DeleteManyUsersAsync_WhenUserNotFound_ReturnsErrorMessage( - SutProvider sutProvider, - Guid organizationId, - Guid orgUserId) - { - // Act - var result = await sutProvider.Sut.DeleteManyUsersAsync(organizationId, new[] { orgUserId }, null); - - // Assert - Assert.Single(result); - Assert.Equal(orgUserId, result.First().Item1); - Assert.Contains("Member not found.", result.First().Item2); - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .DeleteManyAsync(default); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventsAsync(Arg.Any>()); - } - - [Theory] - [BitAutoData] - public async Task DeleteManyUsersAsync_WhenDeletingYourself_ReturnsErrorMessage( - SutProvider sutProvider, - User user, [OrganizationUser] OrganizationUser orgUser, Guid deletingUserId) - { - // Arrange - orgUser.UserId = user.Id = deletingUserId; - - sutProvider.GetDependency() - .GetManyAsync(Arg.Any>()) - .Returns(new List { orgUser }); - - sutProvider.GetDependency() - .GetManyAsync(Arg.Is>(ids => ids.Contains(user.Id))) - .Returns(new[] { user }); - - // Act - var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, deletingUserId); - - // Assert - Assert.Single(result); - Assert.Equal(orgUser.Id, result.First().Item1); - Assert.Contains("You cannot delete yourself.", result.First().Item2); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventsAsync(Arg.Any>()); - } - - [Theory] - [BitAutoData] - public async Task DeleteManyUsersAsync_WhenUserIsInvited_ReturnsErrorMessage( - SutProvider sutProvider, - [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser) - { - // Arrange - orgUser.UserId = null; - - sutProvider.GetDependency() - .GetManyAsync(Arg.Any>()) - .Returns(new List { orgUser }); - - // Act - var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, null); - - // Assert - Assert.Single(result); - Assert.Equal(orgUser.Id, result.First().Item1); - Assert.Contains("You cannot delete a member with Invited status.", result.First().Item2); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventsAsync(Arg.Any>()); - } - - [Theory] - [BitAutoData] - public async Task DeleteManyUsersAsync_WhenDeletingOwnerAsNonOwner_ReturnsErrorMessage( - SutProvider sutProvider, User user, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser, - Guid deletingUserId) - { - // Arrange - orgUser.UserId = user.Id; - - sutProvider.GetDependency() - .GetManyAsync(Arg.Any>()) - .Returns(new List { orgUser }); - - sutProvider.GetDependency() - .GetManyAsync(Arg.Is>(i => i.Contains(user.Id))) - .Returns(new[] { user }); - - sutProvider.GetDependency() - .OrganizationOwner(orgUser.OrganizationId) - .Returns(false); - - var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, deletingUserId); - - Assert.Single(result); - Assert.Equal(orgUser.Id, result.First().Item1); - Assert.Contains("Only owners can delete other owners.", result.First().Item2); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventsAsync(Arg.Any>()); - } - - [Theory] - [BitAutoData] - public async Task DeleteManyUsersAsync_WhenDeletingLastOwner_ReturnsErrorMessage( - SutProvider sutProvider, User user, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser, - Guid deletingUserId) - { - // Arrange - orgUser.UserId = user.Id; - - sutProvider.GetDependency() - .GetManyAsync(Arg.Any>()) - .Returns(new List { orgUser }); - - sutProvider.GetDependency() - .GetManyAsync(Arg.Is>(i => i.Contains(user.Id))) - .Returns(new[] { user }); - - sutProvider.GetDependency() - .OrganizationOwner(orgUser.OrganizationId) - .Returns(true); - - sutProvider.GetDependency() - .HasConfirmedOwnersExceptAsync(orgUser.OrganizationId, Arg.Any>(), Arg.Any()) - .Returns(false); - - // Act - var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, deletingUserId); - - // Assert - Assert.Single(result); - Assert.Equal(orgUser.Id, result.First().Item1); - Assert.Contains("Organization must have at least one confirmed owner.", result.First().Item2); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventsAsync(Arg.Any>()); - } - - [Theory] - [BitAutoData] - public async Task DeleteManyUsersAsync_WhenUserNotManaged_ReturnsErrorMessage( - SutProvider sutProvider, User user, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser) - { - // Arrange - orgUser.UserId = user.Id; - - sutProvider.GetDependency() - .GetManyAsync(Arg.Any>()) - .Returns(new List { orgUser }); - - sutProvider.GetDependency() - .GetManyAsync(Arg.Is>(ids => ids.Contains(orgUser.UserId.Value))) - .Returns(new[] { user }); - - sutProvider.GetDependency() - .GetUsersOrganizationClaimedStatusAsync(Arg.Any(), Arg.Any>()) - .Returns(new Dictionary { { orgUser.Id, false } }); - - // Act - var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, null); - - // Assert - Assert.Single(result); - Assert.Equal(orgUser.Id, result.First().Item1); - Assert.Contains("Member is not claimed by the organization.", result.First().Item2); - await sutProvider.GetDependency().Received(0).DeleteAsync(Arg.Any()); - await sutProvider.GetDependency().Received(0) - .LogOrganizationUserEventsAsync(Arg.Any>()); - } - - [Theory] - [BitAutoData] - public async Task DeleteManyUsersAsync_MixedValidAndInvalidUsers_ReturnsAppropriateResults( - SutProvider sutProvider, User user1, User user3, - Guid organizationId, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser1, - [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser2, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser3) - { - // Arrange - orgUser1.UserId = user1.Id; - orgUser2.UserId = null; - orgUser3.UserId = user3.Id; - orgUser1.OrganizationId = orgUser2.OrganizationId = orgUser3.OrganizationId = organizationId; - - sutProvider.GetDependency() - .GetManyAsync(Arg.Any>()) - .Returns(new List { orgUser1, orgUser2, orgUser3 }); - - sutProvider.GetDependency() - .GetManyAsync(Arg.Is>(ids => ids.Contains(user1.Id) && ids.Contains(user3.Id))) - .Returns(new[] { user1, user3 }); - - sutProvider.GetDependency() - .GetUsersOrganizationClaimedStatusAsync(organizationId, Arg.Any>()) - .Returns(new Dictionary { { orgUser1.Id, true }, { orgUser3.Id, false } }); - - // Act - var results = await sutProvider.Sut.DeleteManyUsersAsync(organizationId, new[] { orgUser1.Id, orgUser2.Id, orgUser3.Id }, null); - - // Assert - Assert.Equal(3, results.Count()); - Assert.Empty(results.First(r => r.Item1 == orgUser1.Id).Item2); - Assert.Equal("You cannot delete a member with Invited status.", results.First(r => r.Item1 == orgUser2.Id).Item2); - Assert.Equal("Member is not claimed by the organization.", results.First(r => r.Item1 == orgUser3.Id).Item2); - - await sutProvider.GetDependency().Received(1).LogOrganizationUserEventsAsync( - Arg.Is>(events => - events.Count(e => e.Item1.Id == orgUser1.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1)); - } -}