mirror of
https://github.com/bitwarden/server.git
synced 2025-12-10 00:42:07 -06:00
[PM-25097] Remove DeleteClaimedUserAccountRefactor flag (#6364)
* Remove feature flag * Remove old code
This commit is contained in:
parent
f0953ed6b0
commit
b83f95f78c
@ -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<ManageUsersRequirement>]
|
||||
public async Task DeleteAccount(Guid orgId, Guid id)
|
||||
public async Task<IResult> 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<IResult>(
|
||||
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<IResult> 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<IResult>(
|
||||
error => error is NotFoundError
|
||||
? TypedResults.NotFound(new ErrorResponseModel(error.Message))
|
||||
: TypedResults.BadRequest(new ErrorResponseModel(error.Message)),
|
||||
TypedResults.Ok
|
||||
);
|
||||
}
|
||||
|
||||
[HttpDelete("delete-account")]
|
||||
[Authorize<ManageUsersRequirement>]
|
||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> 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<OrganizationUserBulkResponseModel>(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<OrganizationUserBulkResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPost("delete-account")]
|
||||
@ -611,24 +590,6 @@ public class OrganizationUsersController : Controller
|
||||
return await BulkDeleteAccount(orgId, model);
|
||||
}
|
||||
|
||||
private async Task<ListResponseModel<OrganizationUserBulkResponseModel>> 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<OrganizationUserBulkResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPut("{id}/revoke")]
|
||||
[Authorize<ManageUsersRequirement>]
|
||||
public async Task RevokeAsync(Guid orgId, Guid id)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using OneOf;
|
||||
using OneOf.Types;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext;
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the result of a command.
|
||||
@ -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<DeleteClaimedOrganizationUserAccountCommandvNext> logger,
|
||||
IDeleteClaimedOrganizationUserAccountValidatorvNext deleteClaimedOrganizationUserAccountValidatorvNext)
|
||||
: IDeleteClaimedOrganizationUserAccountCommandvNext
|
||||
ILogger<DeleteClaimedOrganizationUserAccountCommand> logger,
|
||||
IDeleteClaimedOrganizationUserAccountValidator deleteClaimedOrganizationUserAccountValidator)
|
||||
: IDeleteClaimedOrganizationUserAccountCommand
|
||||
{
|
||||
public async Task<BulkCommandResult> 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);
|
||||
@ -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<IEnumerable<ValidationResult<DeleteUserValidationRequest>>> ValidateAsync(IEnumerable<DeleteUserValidationRequest> requests)
|
||||
{
|
||||
@ -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
|
||||
{
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext;
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount;
|
||||
|
||||
/// <summary>
|
||||
/// A strongly typed error containing a reason that an action failed.
|
||||
@ -1,6 +1,6 @@
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext;
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount;
|
||||
|
||||
public interface IDeleteClaimedOrganizationUserAccountCommandvNext
|
||||
public interface IDeleteClaimedOrganizationUserAccountCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes a user from an organization and deletes all of their associated user data.
|
||||
@ -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<IEnumerable<ValidationResult<DeleteUserValidationRequest>>> ValidateAsync(IEnumerable<DeleteUserValidationRequest> requests);
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
using OneOf;
|
||||
using OneOf.Types;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccountvNext;
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the result of validating a request.
|
||||
@ -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<IEnumerable<(Guid OrganizationUserId, string? ErrorMessage)>> DeleteManyUsersAsync(Guid organizationId, IEnumerable<Guid> 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<Guid, bool> 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<OrganizationUser> 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<User> 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) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
|
||||
public interface IDeleteClaimedOrganizationUserAccountCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes a user from an organization and deletes all of their associated user data.
|
||||
/// </summary>
|
||||
Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Removes multiple users from an organization and deletes all of their associated user data.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An error message for each user that could not be removed, otherwise null.
|
||||
/// </returns>
|
||||
Task<IEnumerable<(Guid OrganizationUserId, string? ErrorMessage)>> DeleteManyUsersAsync(Guid organizationId, IEnumerable<Guid> orgUserIds, Guid? deletingUserId);
|
||||
}
|
||||
@ -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";
|
||||
|
||||
@ -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<IRevokeOrganizationUserCommand, RevokeOrganizationUserCommand>();
|
||||
services.AddScoped<IUpdateOrganizationUserCommand, UpdateOrganizationUserCommand>();
|
||||
services.AddScoped<IUpdateOrganizationUserGroupsCommand, UpdateOrganizationUserGroupsCommand>();
|
||||
services.AddScoped<IDeleteClaimedOrganizationUserAccountCommand, DeleteClaimedOrganizationUserAccountCommand>();
|
||||
services.AddScoped<IConfirmOrganizationUserCommand, ConfirmOrganizationUserCommand>();
|
||||
|
||||
// vNext implementations (feature flagged)
|
||||
services.AddScoped<IDeleteClaimedOrganizationUserAccountCommandvNext, DeleteClaimedOrganizationUserAccountCommandvNext>();
|
||||
services.AddScoped<IDeleteClaimedOrganizationUserAccountValidatorvNext, DeleteClaimedOrganizationUserAccountValidatorvNext>();
|
||||
services.AddScoped<IDeleteClaimedOrganizationUserAccountCommand, DeleteClaimedOrganizationUserAccountCommand>();
|
||||
services.AddScoped<IDeleteClaimedOrganizationUserAccountValidator, DeleteClaimedOrganizationUserAccountValidator>();
|
||||
}
|
||||
|
||||
private static void AddOrganizationApiKeyCommandsQueries(this IServiceCollection services)
|
||||
|
||||
@ -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<ApiApplicationFacto
|
||||
featureService
|
||||
.IsEnabled(FeatureFlagKeys.CreateDefaultLocation)
|
||||
.Returns(true);
|
||||
|
||||
featureService
|
||||
.IsEnabled(FeatureFlagKeys.DeleteClaimedUserAccountRefactor)
|
||||
.Returns(true);
|
||||
});
|
||||
_client = _factory.CreateClient();
|
||||
_loginHelper = new LoginHelper(_factory, _client);
|
||||
|
||||
@ -29,6 +29,7 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
@ -305,29 +306,14 @@ public class OrganizationUsersControllerTests
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteAccount_WhenUserCanManageUsers_Success(
|
||||
Guid orgId, Guid id, User currentUser, SutProvider<OrganizationUsersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(orgId).Returns(true);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(currentUser);
|
||||
|
||||
await sutProvider.Sut.DeleteAccount(orgId, id);
|
||||
|
||||
await sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountCommand>()
|
||||
.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<OrganizationUsersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(orgId).Returns(true);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs((User)null);
|
||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs((Guid?)null);
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(() =>
|
||||
sutProvider.Sut.DeleteAccount(orgId, id));
|
||||
var result = await sutProvider.Sut.DeleteAccount(orgId, id);
|
||||
|
||||
Assert.IsType<UnauthorizedHttpResult>(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
@ -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<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -65,7 +65,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_WithEmptyUserIds_ReturnsEmptyResults(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId)
|
||||
{
|
||||
@ -77,7 +77,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_WithValidUsers_DeletesUsersAndLogsEvents(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
User user1,
|
||||
User user2,
|
||||
Guid organizationId,
|
||||
@ -135,7 +135,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_WithValidationErrors_ReturnsErrorResults(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
Guid organizationId,
|
||||
Guid orgUserId1,
|
||||
Guid orgUserId2,
|
||||
@ -183,7 +183,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_WithMixedValidationResults_HandlesPartialSuccessCorrectly(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
User validUser,
|
||||
Guid organizationId,
|
||||
Guid validOrgUserId,
|
||||
@ -243,7 +243,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_CancelPremiumsAsync_HandlesGatewayExceptionAndLogsWarning(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -285,7 +285,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
await sutProvider.GetDependency<IUserService>().Received(1).CancelPremiumAsync(user);
|
||||
await AssertSuccessfulUserOperations(sutProvider, [user], [orgUser]);
|
||||
|
||||
sutProvider.GetDependency<ILogger<DeleteClaimedOrganizationUserAccountCommandvNext>>()
|
||||
sutProvider.GetDependency<ILogger<DeleteClaimedOrganizationUserAccountCommand>>()
|
||||
.Received(1)
|
||||
.Log(
|
||||
LogLevel.Warning,
|
||||
@ -299,7 +299,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CreateInternalRequests_CreatesCorrectRequestsForAllUsers(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
User user1,
|
||||
User user2,
|
||||
Guid organizationId,
|
||||
@ -326,7 +326,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
.GetUsersOrganizationClaimedStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(claimedStatuses);
|
||||
|
||||
sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountValidatorvNext>()
|
||||
sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountValidator>()
|
||||
.ValidateAsync(Arg.Any<IEnumerable<DeleteUserValidationRequest>>())
|
||||
.Returns(callInfo =>
|
||||
{
|
||||
@ -338,7 +338,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
await sutProvider.Sut.DeleteManyUsersAsync(organizationId, orgUserIds, deletingUserId);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountValidatorvNext>()
|
||||
await sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountValidator>()
|
||||
.Received(1)
|
||||
.ValidateAsync(Arg.Is<IEnumerable<DeleteUserValidationRequest>>(requests =>
|
||||
requests.Count() == 2 &&
|
||||
@ -359,7 +359,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetUsersAsync_WithNullUserIds_ReturnsEmptyCollection(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
[OrganizationUser] OrganizationUser orgUserWithoutUserId)
|
||||
@ -374,7 +374,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => !ids.Any()))
|
||||
.Returns([]);
|
||||
|
||||
sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountValidatorvNext>()
|
||||
sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountValidator>()
|
||||
.ValidateAsync(Arg.Any<IEnumerable<DeleteUserValidationRequest>>())
|
||||
.Returns(callInfo =>
|
||||
{
|
||||
@ -386,7 +386,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
await sutProvider.Sut.DeleteManyUsersAsync(organizationId, [orgUserWithoutUserId.Id], deletingUserId);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountValidatorvNext>()
|
||||
await sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountValidator>()
|
||||
.Received(1)
|
||||
.ValidateAsync(Arg.Is<IEnumerable<DeleteUserValidationRequest>>(requests =>
|
||||
requests.Count() == 1 &&
|
||||
@ -406,7 +406,7 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
ValidationResultHelpers.Invalid(request, error);
|
||||
|
||||
private static void SetupRepositoryMocks(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
ICollection<OrganizationUser> orgUsers,
|
||||
IEnumerable<User> users,
|
||||
Guid organizationId,
|
||||
@ -426,16 +426,16 @@ public class DeleteClaimedOrganizationUserAccountCommandvNextTests
|
||||
}
|
||||
|
||||
private static void SetupValidatorMock(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
IEnumerable<ValidationResult<DeleteUserValidationRequest>> validationResults)
|
||||
{
|
||||
sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountValidatorvNext>()
|
||||
sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountValidator>()
|
||||
.ValidateAsync(Arg.Any<IEnumerable<DeleteUserValidationRequest>>())
|
||||
.Returns(validationResults);
|
||||
}
|
||||
|
||||
private static async Task AssertSuccessfulUserOperations(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
IEnumerable<User> expectedUsers,
|
||||
IEnumerable<OrganizationUser> 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<DeleteClaimedOrganizationUserAccountCommandvNext> sutProvider)
|
||||
private static async Task AssertNoUserOperations(SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider)
|
||||
{
|
||||
await sutProvider.GetDependency<IUserRepository>().DidNotReceiveWithAnyArgs().DeleteManyAsync(default);
|
||||
await sutProvider.GetDependency<IPushNotificationService>().DidNotReceiveWithAnyArgs().PushLogOutAsync(default);
|
||||
@ -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<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -50,7 +50,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_WithMultipleValidRequests_ReturnsAllValidResults(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user1,
|
||||
User user2,
|
||||
Guid organizationId,
|
||||
@ -97,7 +97,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_WithNullUser_ReturnsUserNotFoundError(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
[OrganizationUser] OrganizationUser organizationUser)
|
||||
@ -123,7 +123,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_WithNullOrganizationUser_ReturnsUserNotFoundError(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId)
|
||||
@ -149,7 +149,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_WithInvitedUser_ReturnsInvalidUserStatusError(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -178,7 +178,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_WhenDeletingYourself_ReturnsCannotDeleteYourselfError(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
[OrganizationUser] OrganizationUser organizationUser)
|
||||
@ -206,7 +206,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_WithUnclaimedUser_ReturnsUserNotClaimedError(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -235,7 +235,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_DeletingOwnerWhenCurrentUserIsNotOwner_ReturnsCannotDeleteOwnersError(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -266,7 +266,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_DeletingOwnerWhenCurrentUserIsOwner_ReturnsValidResult(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -296,7 +296,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_WithSoleOwnerOfOrganization_ReturnsSoleOwnerError(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -331,7 +331,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_WithSoleProviderOwner_ReturnsSoleProviderError(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -366,7 +366,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_CustomUserDeletingAdmin_ReturnsCannotDeleteAdminsError(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -397,7 +397,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_AdminDeletingAdmin_ReturnsValidResult(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User user,
|
||||
Guid organizationId,
|
||||
Guid deletingUserId,
|
||||
@ -427,7 +427,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateAsync_WithMixedValidAndInvalidRequests_ReturnsCorrespondingResults(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
User validUser,
|
||||
User invalidUser,
|
||||
Guid organizationId,
|
||||
@ -475,7 +475,7 @@ public class DeleteClaimedOrganizationUserAccountValidatorvNextTests
|
||||
}
|
||||
|
||||
private static void SetupMocks(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidatorvNext> sutProvider,
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountValidator> sutProvider,
|
||||
Guid organizationId,
|
||||
Guid userId,
|
||||
OrganizationUserType currentUserType = OrganizationUserType.Owner)
|
||||
@ -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<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user, Guid deletingUserId,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id;
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUser.Id)
|
||||
.Returns(organizationUser);
|
||||
|
||||
sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.GetUsersOrganizationClaimedStatusAsync(
|
||||
organizationUser.OrganizationId,
|
||||
Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)))
|
||||
.Returns(new Dictionary<Guid, bool> { { organizationUser.Id, true } });
|
||||
|
||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||
.HasConfirmedOwnersExceptAsync(
|
||||
organizationUser.OrganizationId,
|
||||
Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)),
|
||||
includeProvider: Arg.Any<bool>())
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IUserService>().Received(1).DeleteAsync(user);
|
||||
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Deleted);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteUserAsync_WithUserNotFound_ThrowsException(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
Guid organizationId, Guid organizationUserId)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUserId)
|
||||
.Returns((OrganizationUser?)null);
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||
sutProvider.Sut.DeleteUserAsync(organizationId, organizationUserId, null));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Member not found.", exception.Message);
|
||||
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<DateTime?>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteUserAsync_DeletingYourself_ThrowsException(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
User user,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser,
|
||||
Guid deletingUserId)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id = deletingUserId;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUser.Id)
|
||||
.Returns(organizationUser);
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("You cannot delete yourself.", exception.Message);
|
||||
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<DateTime?>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteUserAsync_WhenUserIsInvited_ThrowsException(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
[OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser organizationUser)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = null;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUser.Id)
|
||||
.Returns(organizationUser);
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, null));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("You cannot delete a member with Invited status.", exception.Message);
|
||||
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<DateTime?>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteUserAsync_WhenCustomUserDeletesAdmin_ThrowsException(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Admin)] OrganizationUser organizationUser,
|
||||
Guid deletingUserId)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUser.Id)
|
||||
.Returns(organizationUser);
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationCustom(organizationUser.OrganizationId)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Custom users can not delete admins.", exception.Message);
|
||||
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<DateTime?>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteUserAsync_DeletingOwnerWhenNotOwner_ThrowsException(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser organizationUser,
|
||||
Guid deletingUserId)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUser.Id)
|
||||
.Returns(organizationUser);
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationOwner(organizationUser.OrganizationId)
|
||||
.Returns(false);
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Only owners can delete other owners.", exception.Message);
|
||||
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<DateTime?>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteUserAsync_DeletingLastConfirmedOwner_ThrowsException(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser organizationUser,
|
||||
Guid deletingUserId)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUser.Id)
|
||||
.Returns(organizationUser);
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationOwner(organizationUser.OrganizationId)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||
.HasConfirmedOwnersExceptAsync(
|
||||
organizationUser.OrganizationId,
|
||||
Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)),
|
||||
includeProvider: Arg.Any<bool>())
|
||||
.Returns(false);
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, deletingUserId));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Organization must have at least one confirmed owner.", exception.Message);
|
||||
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<DateTime?>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteUserAsync_WithUserNotManaged_ThrowsException(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = user.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUser.Id)
|
||||
.Returns(organizationUser);
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
|
||||
sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.GetUsersOrganizationClaimedStatusAsync(organizationUser.OrganizationId, Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new Dictionary<Guid, bool> { { organizationUser.Id, false } });
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, null));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Member is not claimed by the organization.", exception.Message);
|
||||
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<DateTime?>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_WithValidUsers_DeletesUsersAndLogsEvents(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> 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<IOrganizationUserRepository>()
|
||||
.GetManyAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new List<OrganizationUser> { orgUser1, orgUser2 });
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user2.Id)))
|
||||
.Returns(new[] { user1, user2 });
|
||||
|
||||
sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.GetUsersOrganizationClaimedStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new Dictionary<Guid, bool> { { 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<IOrganizationUserRepository>().Received(1).GetManyAsync(userIds);
|
||||
await sutProvider.GetDependency<IUserRepository>().Received(1).DeleteManyAsync(Arg.Is<IEnumerable<User>>(users => users.Any(u => u.Id == user1.Id) && users.Any(u => u.Id == user2.Id)));
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(
|
||||
Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(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<DeleteClaimedOrganizationUserAccountCommand> 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<IUserRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_WhenDeletingYourself_ReturnsErrorMessage(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
User user, [OrganizationUser] OrganizationUser orgUser, Guid deletingUserId)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.UserId = user.Id = deletingUserId;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new List<OrganizationUser> { orgUser });
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(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<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_WhenUserIsInvited_ReturnsErrorMessage(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
|
||||
[OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.UserId = null;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new List<OrganizationUser> { 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<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_WhenDeletingOwnerAsNonOwner_ReturnsErrorMessage(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser,
|
||||
Guid deletingUserId)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.UserId = user.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new List<OrganizationUser> { orgUser });
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(user.Id)))
|
||||
.Returns(new[] { user });
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.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<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_WhenDeletingLastOwner_ReturnsErrorMessage(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser,
|
||||
Guid deletingUserId)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.UserId = user.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new List<OrganizationUser> { orgUser });
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(user.Id)))
|
||||
.Returns(new[] { user });
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationOwner(orgUser.OrganizationId)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||
.HasConfirmedOwnersExceptAsync(orgUser.OrganizationId, Arg.Any<IEnumerable<Guid>>(), Arg.Any<bool>())
|
||||
.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<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_WhenUserNotManaged_ReturnsErrorMessage(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.UserId = user.Id;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new List<OrganizationUser> { orgUser });
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser.UserId.Value)))
|
||||
.Returns(new[] { user });
|
||||
|
||||
sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.GetUsersOrganizationClaimedStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new Dictionary<Guid, bool> { { 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<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
|
||||
await sutProvider.GetDependency<IEventService>().Received(0)
|
||||
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteManyUsersAsync_MixedValidAndInvalidUsers_ReturnsAppropriateResults(
|
||||
SutProvider<DeleteClaimedOrganizationUserAccountCommand> 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<IOrganizationUserRepository>()
|
||||
.GetManyAsync(Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new List<OrganizationUser> { orgUser1, orgUser2, orgUser3 });
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user3.Id)))
|
||||
.Returns(new[] { user1, user3 });
|
||||
|
||||
sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.GetUsersOrganizationClaimedStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(new Dictionary<Guid, bool> { { 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<IEventService>().Received(1).LogOrganizationUserEventsAsync(
|
||||
Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events =>
|
||||
events.Count(e => e.Item1.Id == orgUser1.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user