mirror of
https://github.com/bitwarden/server.git
synced 2024-11-24 12:35:25 +01:00
[AC-607] Extract IOrganizationService.DeleteUserAsync into IRemoveOrganizationUserCommand (#4803)
* Add HasConfirmedOwnersExceptQuery class, interface and unit tests * Register IHasConfirmedOwnersExceptQuery for dependency injection * Replace OrganizationService.HasConfirmedOwnersExceptAsync with HasConfirmedOwnersExceptQuery * Refactor DeleteManagedOrganizationUserAccountCommand to use IHasConfirmedOwnersExceptQuery * Fix unit tests * Extract IOrganizationService.RemoveUserAsync into IRemoveOrganizationUserCommand; Update unit tests * Extract IOrganizationService.RemoveUsersAsync into IRemoveOrganizationUserCommand; Update unit tests * Refactor RemoveUserAsync(Guid organizationId, Guid userId) to use ValidateDeleteUser * Refactor RemoveOrganizationUserCommandTests to use more descriptive method names * Refactor controller actions to accept Guid directly instead of parsing strings * Add unit tests for removing OrganizationUser by UserId * Refactor remove OrganizationUser by UserId method * Add summary to IHasConfirmedOwnersExceptQuery
This commit is contained in:
parent
7408f3ee02
commit
93e49ffe74
@ -2,6 +2,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Providers.Interfaces;
|
using Bit.Core.AdminConsole.Providers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
@ -27,6 +28,7 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
private readonly IProviderBillingService _providerBillingService;
|
private readonly IProviderBillingService _providerBillingService;
|
||||||
private readonly ISubscriberService _subscriberService;
|
private readonly ISubscriberService _subscriberService;
|
||||||
|
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
|
||||||
|
|
||||||
public RemoveOrganizationFromProviderCommand(
|
public RemoveOrganizationFromProviderCommand(
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
@ -37,7 +39,8 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
IStripeAdapter stripeAdapter,
|
IStripeAdapter stripeAdapter,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
IProviderBillingService providerBillingService,
|
IProviderBillingService providerBillingService,
|
||||||
ISubscriberService subscriberService)
|
ISubscriberService subscriberService,
|
||||||
|
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery)
|
||||||
{
|
{
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
_mailService = mailService;
|
_mailService = mailService;
|
||||||
@ -48,6 +51,7 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
_providerBillingService = providerBillingService;
|
_providerBillingService = providerBillingService;
|
||||||
_subscriberService = subscriberService;
|
_subscriberService = subscriberService;
|
||||||
|
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveOrganizationFromProvider(
|
public async Task RemoveOrganizationFromProvider(
|
||||||
@ -63,7 +67,7 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
throw new BadRequestException("Failed to remove organization. Please contact support.");
|
throw new BadRequestException("Failed to remove organization. Please contact support.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await _organizationService.HasConfirmedOwnersExceptAsync(
|
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(
|
||||||
providerOrganization.OrganizationId,
|
providerOrganization.OrganizationId,
|
||||||
Array.Empty<Guid>(),
|
Array.Empty<Guid>(),
|
||||||
includeProvider: false))
|
includeProvider: false))
|
||||||
|
@ -3,6 +3,7 @@ using Bit.Core;
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
@ -75,7 +76,7 @@ public class RemoveOrganizationFromProviderCommandTests
|
|||||||
{
|
{
|
||||||
providerOrganization.ProviderId = provider.Id;
|
providerOrganization.ProviderId = provider.Id;
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>().HasConfirmedOwnersExceptAsync(
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>().HasConfirmedOwnersExceptAsync(
|
||||||
providerOrganization.OrganizationId,
|
providerOrganization.OrganizationId,
|
||||||
[],
|
[],
|
||||||
includeProvider: false)
|
includeProvider: false)
|
||||||
@ -98,7 +99,7 @@ public class RemoveOrganizationFromProviderCommandTests
|
|||||||
organization.GatewayCustomerId = null;
|
organization.GatewayCustomerId = null;
|
||||||
organization.GatewaySubscriptionId = null;
|
organization.GatewaySubscriptionId = null;
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>().HasConfirmedOwnersExceptAsync(
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>().HasConfirmedOwnersExceptAsync(
|
||||||
providerOrganization.OrganizationId,
|
providerOrganization.OrganizationId,
|
||||||
[],
|
[],
|
||||||
includeProvider: false)
|
includeProvider: false)
|
||||||
@ -141,7 +142,7 @@ public class RemoveOrganizationFromProviderCommandTests
|
|||||||
{
|
{
|
||||||
providerOrganization.ProviderId = provider.Id;
|
providerOrganization.ProviderId = provider.Id;
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>().HasConfirmedOwnersExceptAsync(
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>().HasConfirmedOwnersExceptAsync(
|
||||||
providerOrganization.OrganizationId,
|
providerOrganization.OrganizationId,
|
||||||
[],
|
[],
|
||||||
includeProvider: false)
|
includeProvider: false)
|
||||||
@ -208,7 +209,7 @@ public class RemoveOrganizationFromProviderCommandTests
|
|||||||
|
|
||||||
var teamsMonthlyPlan = StaticStore.GetPlan(PlanType.TeamsMonthly);
|
var teamsMonthlyPlan = StaticStore.GetPlan(PlanType.TeamsMonthly);
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>().HasConfirmedOwnersExceptAsync(
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>().HasConfirmedOwnersExceptAsync(
|
||||||
providerOrganization.OrganizationId,
|
providerOrganization.OrganizationId,
|
||||||
[],
|
[],
|
||||||
includeProvider: false)
|
includeProvider: false)
|
||||||
|
@ -51,6 +51,7 @@ public class OrganizationUsersController : Controller
|
|||||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||||
private readonly IOrganizationUserUserDetailsQuery _organizationUserUserDetailsQuery;
|
private readonly IOrganizationUserUserDetailsQuery _organizationUserUserDetailsQuery;
|
||||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||||
|
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
||||||
private readonly IDeleteManagedOrganizationUserAccountCommand _deleteManagedOrganizationUserAccountCommand;
|
private readonly IDeleteManagedOrganizationUserAccountCommand _deleteManagedOrganizationUserAccountCommand;
|
||||||
|
|
||||||
public OrganizationUsersController(
|
public OrganizationUsersController(
|
||||||
@ -71,6 +72,7 @@ public class OrganizationUsersController : Controller
|
|||||||
ISsoConfigRepository ssoConfigRepository,
|
ISsoConfigRepository ssoConfigRepository,
|
||||||
IOrganizationUserUserDetailsQuery organizationUserUserDetailsQuery,
|
IOrganizationUserUserDetailsQuery organizationUserUserDetailsQuery,
|
||||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||||
|
IRemoveOrganizationUserCommand removeOrganizationUserCommand,
|
||||||
IDeleteManagedOrganizationUserAccountCommand deleteManagedOrganizationUserAccountCommand)
|
IDeleteManagedOrganizationUserAccountCommand deleteManagedOrganizationUserAccountCommand)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
@ -90,6 +92,7 @@ public class OrganizationUsersController : Controller
|
|||||||
_ssoConfigRepository = ssoConfigRepository;
|
_ssoConfigRepository = ssoConfigRepository;
|
||||||
_organizationUserUserDetailsQuery = organizationUserUserDetailsQuery;
|
_organizationUserUserDetailsQuery = organizationUserUserDetailsQuery;
|
||||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||||
|
_removeOrganizationUserCommand = removeOrganizationUserCommand;
|
||||||
_deleteManagedOrganizationUserAccountCommand = deleteManagedOrganizationUserAccountCommand;
|
_deleteManagedOrganizationUserAccountCommand = deleteManagedOrganizationUserAccountCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,30 +505,28 @@ public class OrganizationUsersController : Controller
|
|||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[HttpPost("{id}/remove")]
|
[HttpPost("{id}/remove")]
|
||||||
public async Task Remove(string orgId, string id)
|
public async Task Remove(Guid orgId, Guid id)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(orgId);
|
if (!await _currentContext.ManageUsers(orgId))
|
||||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var userId = _userService.GetProperUserId(User);
|
var userId = _userService.GetProperUserId(User);
|
||||||
await _organizationService.RemoveUserAsync(orgGuidId, new Guid(id), userId.Value);
|
await _removeOrganizationUserCommand.RemoveUserAsync(orgId, id, userId.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("")]
|
[HttpDelete("")]
|
||||||
[HttpPost("remove")]
|
[HttpPost("remove")]
|
||||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRemove(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRemove(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(orgId);
|
if (!await _currentContext.ManageUsers(orgId))
|
||||||
if (!await _currentContext.ManageUsers(orgGuidId))
|
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var userId = _userService.GetProperUserId(User);
|
var userId = _userService.GetProperUserId(User);
|
||||||
var result = await _organizationService.RemoveUsersAsync(orgGuidId, model.Ids, userId.Value);
|
var result = await _removeOrganizationUserCommand.RemoveUsersAsync(orgId, model.Ids, userId.Value);
|
||||||
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
|
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
|
||||||
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using Bit.Core.AdminConsole.Enums;
|
|||||||
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
using Bit.Core.Auth.Repositories;
|
using Bit.Core.Auth.Repositories;
|
||||||
@ -55,6 +56,7 @@ public class OrganizationsController : Controller
|
|||||||
private readonly IProviderRepository _providerRepository;
|
private readonly IProviderRepository _providerRepository;
|
||||||
private readonly IProviderBillingService _providerBillingService;
|
private readonly IProviderBillingService _providerBillingService;
|
||||||
private readonly IDataProtectorTokenFactory<OrgDeleteTokenable> _orgDeleteTokenDataFactory;
|
private readonly IDataProtectorTokenFactory<OrgDeleteTokenable> _orgDeleteTokenDataFactory;
|
||||||
|
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
||||||
|
|
||||||
public OrganizationsController(
|
public OrganizationsController(
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
@ -74,7 +76,8 @@ public class OrganizationsController : Controller
|
|||||||
IPushNotificationService pushNotificationService,
|
IPushNotificationService pushNotificationService,
|
||||||
IProviderRepository providerRepository,
|
IProviderRepository providerRepository,
|
||||||
IProviderBillingService providerBillingService,
|
IProviderBillingService providerBillingService,
|
||||||
IDataProtectorTokenFactory<OrgDeleteTokenable> orgDeleteTokenDataFactory)
|
IDataProtectorTokenFactory<OrgDeleteTokenable> orgDeleteTokenDataFactory,
|
||||||
|
IRemoveOrganizationUserCommand removeOrganizationUserCommand)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
@ -94,6 +97,7 @@ public class OrganizationsController : Controller
|
|||||||
_providerRepository = providerRepository;
|
_providerRepository = providerRepository;
|
||||||
_providerBillingService = providerBillingService;
|
_providerBillingService = providerBillingService;
|
||||||
_orgDeleteTokenDataFactory = orgDeleteTokenDataFactory;
|
_orgDeleteTokenDataFactory = orgDeleteTokenDataFactory;
|
||||||
|
_removeOrganizationUserCommand = removeOrganizationUserCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
@ -229,24 +233,22 @@ public class OrganizationsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/leave")]
|
[HttpPost("{id}/leave")]
|
||||||
public async Task Leave(string id)
|
public async Task Leave(Guid id)
|
||||||
{
|
{
|
||||||
var orgGuidId = new Guid(id);
|
if (!await _currentContext.OrganizationUser(id))
|
||||||
if (!await _currentContext.OrganizationUser(orgGuidId))
|
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
|
|
||||||
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(orgGuidId);
|
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(id);
|
||||||
if (ssoConfig?.GetData()?.MemberDecryptionType == MemberDecryptionType.KeyConnector && user.UsesKeyConnector)
|
if (ssoConfig?.GetData()?.MemberDecryptionType == MemberDecryptionType.KeyConnector && user.UsesKeyConnector)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Your organization's Single Sign-On settings prevent you from leaving.");
|
throw new BadRequestException("Your organization's Single Sign-On settings prevent you from leaving.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _removeOrganizationUserCommand.RemoveUserAsync(id, user.Id);
|
||||||
await _organizationService.RemoveUserAsync(orgGuidId, user.Id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
|
@ -28,6 +28,7 @@ public class MembersController : Controller
|
|||||||
private readonly IPaymentService _paymentService;
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||||
|
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
||||||
|
|
||||||
public MembersController(
|
public MembersController(
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
@ -40,7 +41,8 @@ public class MembersController : Controller
|
|||||||
IApplicationCacheService applicationCacheService,
|
IApplicationCacheService applicationCacheService,
|
||||||
IPaymentService paymentService,
|
IPaymentService paymentService,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery)
|
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||||
|
IRemoveOrganizationUserCommand removeOrganizationUserCommand)
|
||||||
{
|
{
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_groupRepository = groupRepository;
|
_groupRepository = groupRepository;
|
||||||
@ -53,6 +55,7 @@ public class MembersController : Controller
|
|||||||
_paymentService = paymentService;
|
_paymentService = paymentService;
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||||
|
_removeOrganizationUserCommand = removeOrganizationUserCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -233,7 +236,7 @@ public class MembersController : Controller
|
|||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
await _organizationService.RemoveUserAsync(_currentContext.OrganizationId.Value, id, null);
|
await _removeOrganizationUserCommand.RemoveUserAsync(_currentContext.OrganizationId.Value, id, null);
|
||||||
return new OkResult();
|
return new OkResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ public class TwoFactorController : Controller
|
|||||||
throw new BadRequestException("UserVerificationToken", "User verification failed.");
|
throw new BadRequestException("UserVerificationToken", "User verification failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
await _userService.DisableTwoFactorProviderAsync(user, model.Type.Value, _organizationService);
|
await _userService.DisableTwoFactorProviderAsync(user, model.Type.Value);
|
||||||
return new TwoFactorProviderResponseModel(model.Type.Value, user);
|
return new TwoFactorProviderResponseModel(model.Type.Value, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +396,7 @@ public class TwoFactorController : Controller
|
|||||||
public async Task<TwoFactorProviderResponseModel> PutDisable([FromBody] TwoFactorProviderRequestModel model)
|
public async Task<TwoFactorProviderResponseModel> PutDisable([FromBody] TwoFactorProviderRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await CheckAsync(model, false);
|
var user = await CheckAsync(model, false);
|
||||||
await _userService.DisableTwoFactorProviderAsync(user, model.Type.Value, _organizationService);
|
await _userService.DisableTwoFactorProviderAsync(user, model.Type.Value);
|
||||||
var response = new TwoFactorProviderResponseModel(model.Type.Value, user);
|
var response = new TwoFactorProviderResponseModel(model.Type.Value, user);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@ -437,8 +437,7 @@ public class TwoFactorController : Controller
|
|||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task PostRecover([FromBody] TwoFactorRecoveryRequestModel model)
|
public async Task PostRecover([FromBody] TwoFactorRecoveryRequestModel model)
|
||||||
{
|
{
|
||||||
if (!await _userService.RecoverTwoFactorAsync(model.Email, model.MasterPasswordHash, model.RecoveryCode,
|
if (!await _userService.RecoverTwoFactorAsync(model.Email, model.MasterPasswordHash, model.RecoveryCode))
|
||||||
_organizationService))
|
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
throw new BadRequestException(string.Empty, "Invalid information. Try again.");
|
throw new BadRequestException(string.Empty, "Invalid information. Try again.");
|
||||||
|
@ -18,7 +18,8 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
|
|||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
|
||||||
|
|
||||||
public DeleteManagedOrganizationUserAccountCommand(
|
public DeleteManagedOrganizationUserAccountCommand(
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
@ -26,7 +27,7 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
|
|||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
IOrganizationService organizationService)
|
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery)
|
||||||
{
|
{
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
@ -34,7 +35,7 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
|
|||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_organizationService = organizationService;
|
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
||||||
@ -46,7 +47,7 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
|
|||||||
}
|
}
|
||||||
|
|
||||||
var managementStatus = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(organizationId, new[] { organizationUserId });
|
var managementStatus = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(organizationId, new[] { organizationUserId });
|
||||||
var hasOtherConfirmedOwners = await _organizationService.HasConfirmedOwnersExceptAsync(organizationId, new[] { organizationUserId }, includeProvider: true);
|
var hasOtherConfirmedOwners = await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, new[] { organizationUserId }, includeProvider: true);
|
||||||
|
|
||||||
await ValidateDeleteUserAsync(organizationId, organizationUser, deletingUserId, managementStatus, hasOtherConfirmedOwners);
|
await ValidateDeleteUserAsync(organizationId, organizationUser, deletingUserId, managementStatus, hasOtherConfirmedOwners);
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
|
|||||||
var users = await _userRepository.GetManyAsync(userIds);
|
var users = await _userRepository.GetManyAsync(userIds);
|
||||||
|
|
||||||
var managementStatus = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(organizationId, orgUserIds);
|
var managementStatus = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(organizationId, orgUserIds);
|
||||||
var hasOtherConfirmedOwners = await _organizationService.HasConfirmedOwnersExceptAsync(organizationId, orgUserIds, includeProvider: true);
|
var hasOtherConfirmedOwners = await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, orgUserIds, includeProvider: true);
|
||||||
|
|
||||||
var results = new List<(Guid OrganizationUserId, string? ErrorMessage)>();
|
var results = new List<(Guid OrganizationUserId, string? ErrorMessage)>();
|
||||||
foreach (var orgUserId in orgUserIds)
|
foreach (var orgUserId in orgUserIds)
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||||
|
|
||||||
|
public class HasConfirmedOwnersExceptQuery : IHasConfirmedOwnersExceptQuery
|
||||||
|
{
|
||||||
|
private readonly IProviderUserRepository _providerUserRepository;
|
||||||
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
|
||||||
|
public HasConfirmedOwnersExceptQuery(
|
||||||
|
IProviderUserRepository providerUserRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository)
|
||||||
|
{
|
||||||
|
_providerUserRepository = providerUserRepository;
|
||||||
|
_organizationUserRepository = organizationUserRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId, bool includeProvider = true)
|
||||||
|
{
|
||||||
|
var confirmedOwners = await GetConfirmedOwnersAsync(organizationId);
|
||||||
|
var confirmedOwnersIds = confirmedOwners.Select(u => u.Id);
|
||||||
|
bool hasOtherOwner = confirmedOwnersIds.Except(organizationUsersId).Any();
|
||||||
|
if (!hasOtherOwner && includeProvider)
|
||||||
|
{
|
||||||
|
return (await _providerUserRepository.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)).Any();
|
||||||
|
}
|
||||||
|
return hasOtherOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<OrganizationUser>> GetConfirmedOwnersAsync(Guid organizationId)
|
||||||
|
{
|
||||||
|
var owners = await _organizationUserRepository.GetManyByOrganizationAsync(organizationId,
|
||||||
|
OrganizationUserType.Owner);
|
||||||
|
return owners.Where(o => o.Status == OrganizationUserStatusType.Confirmed);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
|
||||||
|
public interface IHasConfirmedOwnersExceptQuery
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if an organization has any confirmed owners except for the ones in the <paramref name="organizationUsersId"/> list.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="organizationId">The organization ID.</param>
|
||||||
|
/// <param name="organizationUsersId">The organization user IDs to exclude.</param>
|
||||||
|
/// <param name="includeProvider">Whether to include the provider users in the count.</param>
|
||||||
|
Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId, bool includeProvider = true);
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
|
||||||
@ -7,4 +8,7 @@ public interface IRemoveOrganizationUserCommand
|
|||||||
Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId);
|
Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId);
|
||||||
|
|
||||||
Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, EventSystemUser eventSystemUser);
|
Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, EventSystemUser eventSystemUser);
|
||||||
|
Task RemoveUserAsync(Guid organizationId, Guid userId);
|
||||||
|
Task<List<Tuple<OrganizationUser, string>>> RemoveUsersAsync(Guid organizationId,
|
||||||
|
IEnumerable<Guid> organizationUserIds, Guid? deletingUserId);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
@ -8,38 +10,171 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
|||||||
|
|
||||||
public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
|
public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
|
||||||
{
|
{
|
||||||
|
private readonly IDeviceRepository _deviceRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly IEventService _eventService;
|
||||||
|
private readonly IPushNotificationService _pushNotificationService;
|
||||||
|
private readonly IPushRegistrationService _pushRegistrationService;
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
|
||||||
|
|
||||||
public RemoveOrganizationUserCommand(
|
public RemoveOrganizationUserCommand(
|
||||||
|
IDeviceRepository deviceRepository,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IOrganizationService organizationService
|
IEventService eventService,
|
||||||
)
|
IPushNotificationService pushNotificationService,
|
||||||
|
IPushRegistrationService pushRegistrationService,
|
||||||
|
ICurrentContext currentContext,
|
||||||
|
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery)
|
||||||
{
|
{
|
||||||
|
_deviceRepository = deviceRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_organizationService = organizationService;
|
_eventService = eventService;
|
||||||
|
_pushNotificationService = pushNotificationService;
|
||||||
|
_pushRegistrationService = pushRegistrationService;
|
||||||
|
_currentContext = currentContext;
|
||||||
|
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
public async Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
||||||
{
|
{
|
||||||
await ValidateDeleteUserAsync(organizationId, organizationUserId);
|
var organizationUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
||||||
|
ValidateDeleteUser(organizationId, organizationUser);
|
||||||
|
|
||||||
await _organizationService.RemoveUserAsync(organizationId, organizationUserId, deletingUserId);
|
await RepositoryDeleteUserAsync(organizationUser, deletingUserId);
|
||||||
|
|
||||||
|
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, EventSystemUser eventSystemUser)
|
public async Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, EventSystemUser eventSystemUser)
|
||||||
{
|
{
|
||||||
await ValidateDeleteUserAsync(organizationId, organizationUserId);
|
var organizationUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
||||||
|
ValidateDeleteUser(organizationId, organizationUser);
|
||||||
|
|
||||||
await _organizationService.RemoveUserAsync(organizationId, organizationUserId, eventSystemUser);
|
await RepositoryDeleteUserAsync(organizationUser, null);
|
||||||
|
|
||||||
|
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ValidateDeleteUserAsync(Guid organizationId, Guid organizationUserId)
|
public async Task RemoveUserAsync(Guid organizationId, Guid userId)
|
||||||
|
{
|
||||||
|
var organizationUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, userId);
|
||||||
|
ValidateDeleteUser(organizationId, organizationUser);
|
||||||
|
|
||||||
|
await RepositoryDeleteUserAsync(organizationUser, null);
|
||||||
|
|
||||||
|
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<Tuple<OrganizationUser, string>>> RemoveUsersAsync(Guid organizationId,
|
||||||
|
IEnumerable<Guid> organizationUsersId,
|
||||||
|
Guid? deletingUserId)
|
||||||
|
{
|
||||||
|
var orgUsers = await _organizationUserRepository.GetManyAsync(organizationUsersId);
|
||||||
|
var filteredUsers = orgUsers.Where(u => u.OrganizationId == organizationId)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (!filteredUsers.Any())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Users invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var deletingUserIsOwner = false;
|
||||||
|
if (deletingUserId.HasValue)
|
||||||
|
{
|
||||||
|
deletingUserIsOwner = await _currentContext.OrganizationOwner(organizationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new List<Tuple<OrganizationUser, string>>();
|
||||||
|
var deletedUserIds = new List<Guid>();
|
||||||
|
foreach (var orgUser in filteredUsers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (deletingUserId.HasValue && orgUser.UserId == deletingUserId)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("You cannot remove yourself.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orgUser.Type == OrganizationUserType.Owner && deletingUserId.HasValue && !deletingUserIsOwner)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Only owners can delete other owners.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Removed);
|
||||||
|
|
||||||
|
if (orgUser.UserId.HasValue)
|
||||||
|
{
|
||||||
|
await DeleteAndPushUserRegistrationAsync(organizationId, orgUser.UserId.Value);
|
||||||
|
}
|
||||||
|
result.Add(Tuple.Create(orgUser, ""));
|
||||||
|
deletedUserIds.Add(orgUser.Id);
|
||||||
|
}
|
||||||
|
catch (BadRequestException e)
|
||||||
|
{
|
||||||
|
result.Add(Tuple.Create(orgUser, e.Message));
|
||||||
|
}
|
||||||
|
|
||||||
|
await _organizationUserRepository.DeleteManyAsync(deletedUserIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateDeleteUser(Guid organizationId, OrganizationUser orgUser)
|
||||||
{
|
{
|
||||||
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
|
||||||
if (orgUser == null || orgUser.OrganizationId != organizationId)
|
if (orgUser == null || orgUser.OrganizationId != organizationId)
|
||||||
{
|
{
|
||||||
throw new NotFoundException("User not found.");
|
throw new NotFoundException("User not found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task RepositoryDeleteUserAsync(OrganizationUser orgUser, Guid? deletingUserId)
|
||||||
|
{
|
||||||
|
if (deletingUserId.HasValue && orgUser.UserId == deletingUserId.Value)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("You cannot remove yourself.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orgUser.Type == OrganizationUserType.Owner)
|
||||||
|
{
|
||||||
|
if (deletingUserId.HasValue && !await _currentContext.OrganizationOwner(orgUser.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Only owners can delete other owners.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(orgUser.OrganizationId, new[] { orgUser.Id }, includeProvider: true))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await _organizationUserRepository.DeleteAsync(orgUser);
|
||||||
|
|
||||||
|
if (orgUser.UserId.HasValue)
|
||||||
|
{
|
||||||
|
await DeleteAndPushUserRegistrationAsync(orgUser.OrganizationId, orgUser.UserId.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<KeyValuePair<string, DeviceType>>> GetUserDeviceIdsAsync(Guid userId)
|
||||||
|
{
|
||||||
|
var devices = await _deviceRepository.GetManyByUserIdAsync(userId);
|
||||||
|
return devices
|
||||||
|
.Where(d => !string.IsNullOrWhiteSpace(d.PushToken))
|
||||||
|
.Select(d => new KeyValuePair<string, DeviceType>(d.Id.ToString(), d.Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAndPushUserRegistrationAsync(Guid organizationId, Guid userId)
|
||||||
|
{
|
||||||
|
var devices = await GetUserDeviceIdsAsync(userId);
|
||||||
|
await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(devices,
|
||||||
|
organizationId.ToString());
|
||||||
|
await _pushNotificationService.PushSyncOrgKeysAsync(userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ public class UpdateOrganizationUserCommand : IUpdateOrganizationUserCommand
|
|||||||
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
|
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
|
||||||
private readonly ICollectionRepository _collectionRepository;
|
private readonly ICollectionRepository _collectionRepository;
|
||||||
private readonly IGroupRepository _groupRepository;
|
private readonly IGroupRepository _groupRepository;
|
||||||
|
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
|
||||||
|
|
||||||
public UpdateOrganizationUserCommand(
|
public UpdateOrganizationUserCommand(
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
@ -31,7 +32,8 @@ public class UpdateOrganizationUserCommand : IUpdateOrganizationUserCommand
|
|||||||
ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery,
|
ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery,
|
||||||
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
||||||
ICollectionRepository collectionRepository,
|
ICollectionRepository collectionRepository,
|
||||||
IGroupRepository groupRepository)
|
IGroupRepository groupRepository,
|
||||||
|
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery)
|
||||||
{
|
{
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
@ -41,6 +43,7 @@ public class UpdateOrganizationUserCommand : IUpdateOrganizationUserCommand
|
|||||||
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
||||||
_collectionRepository = collectionRepository;
|
_collectionRepository = collectionRepository;
|
||||||
_groupRepository = groupRepository;
|
_groupRepository = groupRepository;
|
||||||
|
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -87,7 +90,7 @@ public class UpdateOrganizationUserCommand : IUpdateOrganizationUserCommand
|
|||||||
await _organizationService.ValidateOrganizationCustomPermissionsEnabledAsync(user.OrganizationId, user.Type);
|
await _organizationService.ValidateOrganizationCustomPermissionsEnabledAsync(user.OrganizationId, user.Type);
|
||||||
|
|
||||||
if (user.Type != OrganizationUserType.Owner &&
|
if (user.Type != OrganizationUserType.Owner &&
|
||||||
!await _organizationService.HasConfirmedOwnersExceptAsync(user.OrganizationId, new[] { user.Id }))
|
!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(user.OrganizationId, new[] { user.Id }))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||||
}
|
}
|
||||||
|
@ -52,20 +52,12 @@ public interface IOrganizationService
|
|||||||
Task<OrganizationUser> ConfirmUserAsync(Guid organizationId, Guid organizationUserId, string key, Guid confirmingUserId);
|
Task<OrganizationUser> ConfirmUserAsync(Guid organizationId, Guid organizationUserId, string key, Guid confirmingUserId);
|
||||||
Task<List<Tuple<OrganizationUser, string>>> ConfirmUsersAsync(Guid organizationId, Dictionary<Guid, string> keys,
|
Task<List<Tuple<OrganizationUser, string>>> ConfirmUsersAsync(Guid organizationId, Dictionary<Guid, string> keys,
|
||||||
Guid confirmingUserId);
|
Guid confirmingUserId);
|
||||||
[Obsolete("IRemoveOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
|
||||||
Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId);
|
|
||||||
[Obsolete("IRemoveOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
|
||||||
Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, EventSystemUser systemUser);
|
|
||||||
Task RemoveUserAsync(Guid organizationId, Guid userId);
|
|
||||||
Task<List<Tuple<OrganizationUser, string>>> RemoveUsersAsync(Guid organizationId,
|
|
||||||
IEnumerable<Guid> organizationUserIds, Guid? deletingUserId);
|
|
||||||
Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId);
|
Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId);
|
||||||
Task ImportAsync(Guid organizationId, IEnumerable<ImportedGroup> groups,
|
Task ImportAsync(Guid organizationId, IEnumerable<ImportedGroup> groups,
|
||||||
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
|
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
|
||||||
bool overwriteExisting, EventSystemUser eventSystemUser);
|
bool overwriteExisting, EventSystemUser eventSystemUser);
|
||||||
Task DeleteSsoUserAsync(Guid userId, Guid? organizationId);
|
Task DeleteSsoUserAsync(Guid userId, Guid? organizationId);
|
||||||
Task<Organization> UpdateOrganizationKeysAsync(Guid orgId, string publicKey, string privateKey);
|
Task<Organization> UpdateOrganizationKeysAsync(Guid orgId, string publicKey, string privateKey);
|
||||||
Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId, bool includeProvider = true);
|
|
||||||
Task RevokeUserAsync(OrganizationUser organizationUser, Guid? revokingUserId);
|
Task RevokeUserAsync(OrganizationUser organizationUser, Guid? revokingUserId);
|
||||||
Task RevokeUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser);
|
Task RevokeUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser);
|
||||||
Task<List<Tuple<OrganizationUser, string>>> RevokeUsersAsync(Guid organizationId,
|
Task<List<Tuple<OrganizationUser, string>>> RevokeUsersAsync(Guid organizationId,
|
||||||
|
@ -73,6 +73,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||||
private readonly IOrganizationBillingService _organizationBillingService;
|
private readonly IOrganizationBillingService _organizationBillingService;
|
||||||
|
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
|
||||||
|
|
||||||
public OrganizationService(
|
public OrganizationService(
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
@ -107,7 +108,8 @@ public class OrganizationService : IOrganizationService
|
|||||||
IProviderRepository providerRepository,
|
IProviderRepository providerRepository,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||||
IOrganizationBillingService organizationBillingService)
|
IOrganizationBillingService organizationBillingService,
|
||||||
|
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
@ -142,6 +144,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||||
_organizationBillingService = organizationBillingService;
|
_organizationBillingService = organizationBillingService;
|
||||||
|
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
|
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
|
||||||
@ -1074,7 +1077,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invitedAreAllOwners = invites.All(i => i.invite.Type == OrganizationUserType.Owner);
|
var invitedAreAllOwners = invites.All(i => i.invite.Type == OrganizationUserType.Owner);
|
||||||
if (!invitedAreAllOwners && !await HasConfirmedOwnersExceptAsync(organizationId, new Guid[] { }, includeProvider: true))
|
if (!invitedAreAllOwners && !await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, new Guid[] { }, includeProvider: true))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||||
}
|
}
|
||||||
@ -1524,149 +1527,6 @@ public class OrganizationService : IOrganizationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("IRemoveOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
|
||||||
public async Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
|
||||||
{
|
|
||||||
var orgUser = await RepositoryDeleteUserAsync(organizationId, organizationUserId, deletingUserId);
|
|
||||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Removed);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("IRemoveOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
|
||||||
public async Task RemoveUserAsync(Guid organizationId, Guid organizationUserId,
|
|
||||||
EventSystemUser systemUser)
|
|
||||||
{
|
|
||||||
var orgUser = await RepositoryDeleteUserAsync(organizationId, organizationUserId, null);
|
|
||||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Removed, systemUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<OrganizationUser> RepositoryDeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
|
||||||
{
|
|
||||||
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
|
||||||
if (orgUser == null || orgUser.OrganizationId != organizationId)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("User not valid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deletingUserId.HasValue && orgUser.UserId == deletingUserId.Value)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("You cannot remove yourself.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orgUser.Type == OrganizationUserType.Owner && deletingUserId.HasValue &&
|
|
||||||
!await _currentContext.OrganizationOwner(organizationId))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Only owners can delete other owners.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await HasConfirmedOwnersExceptAsync(organizationId, new[] { organizationUserId }, includeProvider: true))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await _organizationUserRepository.DeleteAsync(orgUser);
|
|
||||||
|
|
||||||
if (orgUser.UserId.HasValue)
|
|
||||||
{
|
|
||||||
await DeleteAndPushUserRegistrationAsync(organizationId, orgUser.UserId.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return orgUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RemoveUserAsync(Guid organizationId, Guid userId)
|
|
||||||
{
|
|
||||||
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, userId);
|
|
||||||
if (orgUser == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await HasConfirmedOwnersExceptAsync(organizationId, new[] { orgUser.Id }))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await _organizationUserRepository.DeleteAsync(orgUser);
|
|
||||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Removed);
|
|
||||||
|
|
||||||
if (orgUser.UserId.HasValue)
|
|
||||||
{
|
|
||||||
await DeleteAndPushUserRegistrationAsync(organizationId, orgUser.UserId.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<Tuple<OrganizationUser, string>>> RemoveUsersAsync(Guid organizationId,
|
|
||||||
IEnumerable<Guid> organizationUsersId,
|
|
||||||
Guid? deletingUserId)
|
|
||||||
{
|
|
||||||
var orgUsers = await _organizationUserRepository.GetManyAsync(organizationUsersId);
|
|
||||||
var filteredUsers = orgUsers.Where(u => u.OrganizationId == organizationId)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (!filteredUsers.Any())
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Users invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var deletingUserIsOwner = false;
|
|
||||||
if (deletingUserId.HasValue)
|
|
||||||
{
|
|
||||||
deletingUserIsOwner = await _currentContext.OrganizationOwner(organizationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = new List<Tuple<OrganizationUser, string>>();
|
|
||||||
var deletedUserIds = new List<Guid>();
|
|
||||||
foreach (var orgUser in filteredUsers)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (deletingUserId.HasValue && orgUser.UserId == deletingUserId)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("You cannot remove yourself.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orgUser.Type == OrganizationUserType.Owner && deletingUserId.HasValue && !deletingUserIsOwner)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Only owners can delete other owners.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Removed);
|
|
||||||
|
|
||||||
if (orgUser.UserId.HasValue)
|
|
||||||
{
|
|
||||||
await DeleteAndPushUserRegistrationAsync(organizationId, orgUser.UserId.Value);
|
|
||||||
}
|
|
||||||
result.Add(Tuple.Create(orgUser, ""));
|
|
||||||
deletedUserIds.Add(orgUser.Id);
|
|
||||||
}
|
|
||||||
catch (BadRequestException e)
|
|
||||||
{
|
|
||||||
result.Add(Tuple.Create(orgUser, e.Message));
|
|
||||||
}
|
|
||||||
|
|
||||||
await _organizationUserRepository.DeleteManyAsync(deletedUserIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId, bool includeProvider = true)
|
|
||||||
{
|
|
||||||
var confirmedOwners = await GetConfirmedOwnersAsync(organizationId);
|
|
||||||
var confirmedOwnersIds = confirmedOwners.Select(u => u.Id);
|
|
||||||
bool hasOtherOwner = confirmedOwnersIds.Except(organizationUsersId).Any();
|
|
||||||
if (!hasOtherOwner && includeProvider)
|
|
||||||
{
|
|
||||||
return (await _providerUserRepository.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)).Any();
|
|
||||||
}
|
|
||||||
return hasOtherOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId)
|
public async Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId)
|
||||||
{
|
{
|
||||||
// Org User must be the same as the calling user and the organization ID associated with the user must match passed org ID
|
// Org User must be the same as the calling user and the organization ID associated with the user must match passed org ID
|
||||||
@ -1963,13 +1823,6 @@ public class OrganizationService : IOrganizationService
|
|||||||
await _groupRepository.UpdateUsersAsync(group.Id, users);
|
await _groupRepository.UpdateUsersAsync(group.Id, users);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<OrganizationUser>> GetConfirmedOwnersAsync(Guid organizationId)
|
|
||||||
{
|
|
||||||
var owners = await _organizationUserRepository.GetManyByOrganizationAsync(organizationId,
|
|
||||||
OrganizationUserType.Owner);
|
|
||||||
return owners.Where(o => o.Status == OrganizationUserStatusType.Confirmed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteAndPushUserRegistrationAsync(Guid organizationId, Guid userId)
|
private async Task DeleteAndPushUserRegistrationAsync(Guid organizationId, Guid userId)
|
||||||
{
|
{
|
||||||
var devices = await GetUserDeviceIdsAsync(userId);
|
var devices = await GetUserDeviceIdsAsync(userId);
|
||||||
@ -2274,7 +2127,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
throw new BadRequestException("Already revoked.");
|
throw new BadRequestException("Already revoked.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await HasConfirmedOwnersExceptAsync(organizationUser.OrganizationId, new[] { organizationUser.Id }, includeProvider: true))
|
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationUser.OrganizationId, new[] { organizationUser.Id }, includeProvider: true))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||||
}
|
}
|
||||||
@ -2295,7 +2148,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
throw new BadRequestException("Users invalid.");
|
throw new BadRequestException("Users invalid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await HasConfirmedOwnersExceptAsync(organizationId, organizationUserIds))
|
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, organizationUserIds))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Enums;
|
using Bit.Core.AdminConsole.Enums;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
using Bit.Core.Auth.Repositories;
|
using Bit.Core.Auth.Repositories;
|
||||||
@ -26,6 +27,7 @@ public class PolicyService : IPolicyService
|
|||||||
private readonly IMailService _mailService;
|
private readonly IMailService _mailService;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||||
|
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
||||||
|
|
||||||
public PolicyService(
|
public PolicyService(
|
||||||
IApplicationCacheService applicationCacheService,
|
IApplicationCacheService applicationCacheService,
|
||||||
@ -36,7 +38,8 @@ public class PolicyService : IPolicyService
|
|||||||
ISsoConfigRepository ssoConfigRepository,
|
ISsoConfigRepository ssoConfigRepository,
|
||||||
IMailService mailService,
|
IMailService mailService,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery)
|
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||||
|
IRemoveOrganizationUserCommand removeOrganizationUserCommand)
|
||||||
{
|
{
|
||||||
_applicationCacheService = applicationCacheService;
|
_applicationCacheService = applicationCacheService;
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
@ -47,6 +50,7 @@ public class PolicyService : IPolicyService
|
|||||||
_mailService = mailService;
|
_mailService = mailService;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||||
|
_removeOrganizationUserCommand = removeOrganizationUserCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveAsync(Policy policy, IOrganizationService organizationService, Guid? savingUserId)
|
public async Task SaveAsync(Policy policy, IOrganizationService organizationService, Guid? savingUserId)
|
||||||
@ -284,7 +288,7 @@ public class PolicyService : IPolicyService
|
|||||||
"Policy could not be enabled. Non-compliant members will lose access to their accounts. Identify members without two-step login from the policies column in the members page.");
|
"Policy could not be enabled. Non-compliant members will lose access to their accounts. Identify members without two-step login from the policies column in the members page.");
|
||||||
}
|
}
|
||||||
|
|
||||||
await organizationService.RemoveUserAsync(policy.OrganizationId, orgUser.Id,
|
await _removeOrganizationUserCommand.RemoveUserAsync(policy.OrganizationId, orgUser.Id,
|
||||||
savingUserId);
|
savingUserId);
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
||||||
org.DisplayName(), orgUser.Email);
|
org.DisplayName(), orgUser.Email);
|
||||||
@ -300,7 +304,7 @@ public class PolicyService : IPolicyService
|
|||||||
&& ou.OrganizationId != org.Id
|
&& ou.OrganizationId != org.Id
|
||||||
&& ou.Status != OrganizationUserStatusType.Invited))
|
&& ou.Status != OrganizationUserStatusType.Invited))
|
||||||
{
|
{
|
||||||
await organizationService.RemoveUserAsync(policy.OrganizationId, orgUser.Id,
|
await _removeOrganizationUserCommand.RemoveUserAsync(policy.OrganizationId, orgUser.Id,
|
||||||
savingUserId);
|
savingUserId);
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(
|
await _mailService.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(
|
||||||
org.DisplayName(), orgUser.Email);
|
org.DisplayName(), orgUser.Email);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Auth.Entities;
|
using Bit.Core.Auth.Entities;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
@ -33,6 +34,7 @@ public class EmergencyAccessService : IEmergencyAccessService
|
|||||||
private readonly IPasswordHasher<User> _passwordHasher;
|
private readonly IPasswordHasher<User> _passwordHasher;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly IOrganizationService _organizationService;
|
||||||
private readonly IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> _dataProtectorTokenizer;
|
private readonly IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> _dataProtectorTokenizer;
|
||||||
|
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
||||||
|
|
||||||
public EmergencyAccessService(
|
public EmergencyAccessService(
|
||||||
IEmergencyAccessRepository emergencyAccessRepository,
|
IEmergencyAccessRepository emergencyAccessRepository,
|
||||||
@ -46,7 +48,8 @@ public class EmergencyAccessService : IEmergencyAccessService
|
|||||||
IPasswordHasher<User> passwordHasher,
|
IPasswordHasher<User> passwordHasher,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IOrganizationService organizationService,
|
IOrganizationService organizationService,
|
||||||
IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> dataProtectorTokenizer)
|
IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> dataProtectorTokenizer,
|
||||||
|
IRemoveOrganizationUserCommand removeOrganizationUserCommand)
|
||||||
{
|
{
|
||||||
_emergencyAccessRepository = emergencyAccessRepository;
|
_emergencyAccessRepository = emergencyAccessRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
@ -60,6 +63,7 @@ public class EmergencyAccessService : IEmergencyAccessService
|
|||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
_dataProtectorTokenizer = dataProtectorTokenizer;
|
_dataProtectorTokenizer = dataProtectorTokenizer;
|
||||||
|
_removeOrganizationUserCommand = removeOrganizationUserCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EmergencyAccess> InviteAsync(User invitingUser, string email, EmergencyAccessType type, int waitTime)
|
public async Task<EmergencyAccess> InviteAsync(User invitingUser, string email, EmergencyAccessType type, int waitTime)
|
||||||
@ -341,7 +345,7 @@ public class EmergencyAccessService : IEmergencyAccessService
|
|||||||
{
|
{
|
||||||
if (o.Type != OrganizationUserType.Owner)
|
if (o.Type != OrganizationUserType.Owner)
|
||||||
{
|
{
|
||||||
await _organizationService.RemoveUserAsync(o.OrganizationId, grantor.Id);
|
await _removeOrganizationUserCommand.RemoveUserAsync(o.OrganizationId, grantor.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,7 @@ public static class OrganizationServiceCollectionExtensions
|
|||||||
|
|
||||||
services.AddScoped<IAuthorizationHandler, OrganizationUserUserMiniDetailsAuthorizationHandler>();
|
services.AddScoped<IAuthorizationHandler, OrganizationUserUserMiniDetailsAuthorizationHandler>();
|
||||||
services.AddScoped<IAuthorizationHandler, OrganizationUserUserDetailsAuthorizationHandler>();
|
services.AddScoped<IAuthorizationHandler, OrganizationUserUserDetailsAuthorizationHandler>();
|
||||||
|
services.AddScoped<IHasConfirmedOwnersExceptQuery, HasConfirmedOwnersExceptQuery>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to OrganizationSubscriptionServiceCollectionExtensions when OrganizationUser methods are moved out of
|
// TODO: move to OrganizationSubscriptionServiceCollectionExtensions when OrganizationUser methods are moved out of
|
||||||
|
@ -40,10 +40,8 @@ public interface IUserService
|
|||||||
KdfType kdf, int kdfIterations, int? kdfMemory, int? kdfParallelism);
|
KdfType kdf, int kdfIterations, int? kdfMemory, int? kdfParallelism);
|
||||||
Task<IdentityResult> RefreshSecurityStampAsync(User user, string masterPasswordHash);
|
Task<IdentityResult> RefreshSecurityStampAsync(User user, string masterPasswordHash);
|
||||||
Task UpdateTwoFactorProviderAsync(User user, TwoFactorProviderType type, bool setEnabled = true, bool logEvent = true);
|
Task UpdateTwoFactorProviderAsync(User user, TwoFactorProviderType type, bool setEnabled = true, bool logEvent = true);
|
||||||
Task DisableTwoFactorProviderAsync(User user, TwoFactorProviderType type,
|
Task DisableTwoFactorProviderAsync(User user, TwoFactorProviderType type);
|
||||||
IOrganizationService organizationService);
|
Task<bool> RecoverTwoFactorAsync(string email, string masterPassword, string recoveryCode);
|
||||||
Task<bool> RecoverTwoFactorAsync(string email, string masterPassword, string recoveryCode,
|
|
||||||
IOrganizationService organizationService);
|
|
||||||
Task<string> GenerateUserTokenAsync(User user, string tokenProvider, string purpose);
|
Task<string> GenerateUserTokenAsync(User user, string tokenProvider, string purpose);
|
||||||
Task<IdentityResult> DeleteAsync(User user);
|
Task<IdentityResult> DeleteAsync(User user);
|
||||||
Task<IdentityResult> DeleteAsync(User user, string token);
|
Task<IdentityResult> DeleteAsync(User user, string token);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Enums;
|
using Bit.Core.AdminConsole.Enums;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
@ -65,6 +66,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
private readonly IPremiumUserBillingService _premiumUserBillingService;
|
private readonly IPremiumUserBillingService _premiumUserBillingService;
|
||||||
|
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
||||||
|
|
||||||
public UserService(
|
public UserService(
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
@ -98,7 +100,8 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
IStripeSyncService stripeSyncService,
|
IStripeSyncService stripeSyncService,
|
||||||
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
IPremiumUserBillingService premiumUserBillingService)
|
IPremiumUserBillingService premiumUserBillingService,
|
||||||
|
IRemoveOrganizationUserCommand removeOrganizationUserCommand)
|
||||||
: base(
|
: base(
|
||||||
store,
|
store,
|
||||||
optionsAccessor,
|
optionsAccessor,
|
||||||
@ -138,6 +141,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
_premiumUserBillingService = premiumUserBillingService;
|
_premiumUserBillingService = premiumUserBillingService;
|
||||||
|
_removeOrganizationUserCommand = removeOrganizationUserCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid? GetProperUserId(ClaimsPrincipal principal)
|
public Guid? GetProperUserId(ClaimsPrincipal principal)
|
||||||
@ -827,8 +831,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DisableTwoFactorProviderAsync(User user, TwoFactorProviderType type,
|
public async Task DisableTwoFactorProviderAsync(User user, TwoFactorProviderType type)
|
||||||
IOrganizationService organizationService)
|
|
||||||
{
|
{
|
||||||
var providers = user.GetTwoFactorProviders();
|
var providers = user.GetTwoFactorProviders();
|
||||||
if (!providers?.ContainsKey(type) ?? true)
|
if (!providers?.ContainsKey(type) ?? true)
|
||||||
@ -843,12 +846,11 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
|
|
||||||
if (!await TwoFactorIsEnabledAsync(user))
|
if (!await TwoFactorIsEnabledAsync(user))
|
||||||
{
|
{
|
||||||
await CheckPoliciesOnTwoFactorRemovalAsync(user, organizationService);
|
await CheckPoliciesOnTwoFactorRemovalAsync(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> RecoverTwoFactorAsync(string email, string secret, string recoveryCode,
|
public async Task<bool> RecoverTwoFactorAsync(string email, string secret, string recoveryCode)
|
||||||
IOrganizationService organizationService)
|
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByEmailAsync(email);
|
var user = await _userRepository.GetByEmailAsync(email);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
@ -872,7 +874,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
await SaveUserAsync(user);
|
await SaveUserAsync(user);
|
||||||
await _mailService.SendRecoverTwoFactorEmail(user.Email, DateTime.UtcNow, _currentContext.IpAddress);
|
await _mailService.SendRecoverTwoFactorEmail(user.Email, DateTime.UtcNow, _currentContext.IpAddress);
|
||||||
await _eventService.LogUserEventAsync(user.Id, EventType.User_Recovered2fa);
|
await _eventService.LogUserEventAsync(user.Id, EventType.User_Recovered2fa);
|
||||||
await CheckPoliciesOnTwoFactorRemovalAsync(user, organizationService);
|
await CheckPoliciesOnTwoFactorRemovalAsync(user);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1327,13 +1329,13 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckPoliciesOnTwoFactorRemovalAsync(User user, IOrganizationService organizationService)
|
private async Task CheckPoliciesOnTwoFactorRemovalAsync(User user)
|
||||||
{
|
{
|
||||||
var twoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication);
|
var twoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication);
|
||||||
|
|
||||||
var removeOrgUserTasks = twoFactorPolicies.Select(async p =>
|
var removeOrgUserTasks = twoFactorPolicies.Select(async p =>
|
||||||
{
|
{
|
||||||
await organizationService.RemoveUserAsync(p.OrganizationId, user.Id);
|
await _removeOrganizationUserCommand.RemoveUserAsync(p.OrganizationId, user.Id);
|
||||||
var organization = await _organizationRepository.GetByIdAsync(p.OrganizationId);
|
var organization = await _organizationRepository.GetByIdAsync(p.OrganizationId);
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
||||||
organization.DisplayName(), user.Email);
|
organization.DisplayName(), user.Email);
|
||||||
|
@ -7,6 +7,7 @@ using Bit.Core.AdminConsole.Entities;
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Auth.Entities;
|
using Bit.Core.Auth.Entities;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
@ -49,6 +50,7 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
private readonly IProviderRepository _providerRepository;
|
private readonly IProviderRepository _providerRepository;
|
||||||
private readonly IProviderBillingService _providerBillingService;
|
private readonly IProviderBillingService _providerBillingService;
|
||||||
private readonly IDataProtectorTokenFactory<OrgDeleteTokenable> _orgDeleteTokenDataFactory;
|
private readonly IDataProtectorTokenFactory<OrgDeleteTokenable> _orgDeleteTokenDataFactory;
|
||||||
|
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
||||||
|
|
||||||
private readonly OrganizationsController _sut;
|
private readonly OrganizationsController _sut;
|
||||||
|
|
||||||
@ -72,6 +74,7 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
_providerRepository = Substitute.For<IProviderRepository>();
|
_providerRepository = Substitute.For<IProviderRepository>();
|
||||||
_providerBillingService = Substitute.For<IProviderBillingService>();
|
_providerBillingService = Substitute.For<IProviderBillingService>();
|
||||||
_orgDeleteTokenDataFactory = Substitute.For<IDataProtectorTokenFactory<OrgDeleteTokenable>>();
|
_orgDeleteTokenDataFactory = Substitute.For<IDataProtectorTokenFactory<OrgDeleteTokenable>>();
|
||||||
|
_removeOrganizationUserCommand = Substitute.For<IRemoveOrganizationUserCommand>();
|
||||||
|
|
||||||
_sut = new OrganizationsController(
|
_sut = new OrganizationsController(
|
||||||
_organizationRepository,
|
_organizationRepository,
|
||||||
@ -91,7 +94,8 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
_pushNotificationService,
|
_pushNotificationService,
|
||||||
_providerRepository,
|
_providerRepository,
|
||||||
_providerBillingService,
|
_providerBillingService,
|
||||||
_orgDeleteTokenDataFactory);
|
_orgDeleteTokenDataFactory,
|
||||||
|
_removeOrganizationUserCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@ -120,13 +124,12 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
||||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Leave(orgId));
|
||||||
() => _sut.Leave(orgId.ToString()));
|
|
||||||
|
|
||||||
Assert.Contains("Your organization's Single Sign-On settings prevent you from leaving.",
|
Assert.Contains("Your organization's Single Sign-On settings prevent you from leaving.",
|
||||||
exception.Message);
|
exception.Message);
|
||||||
|
|
||||||
await _organizationService.DidNotReceiveWithAnyArgs().RemoveUserAsync(default, default);
|
await _removeOrganizationUserCommand.DidNotReceiveWithAnyArgs().RemoveUserAsync(default, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -155,8 +158,9 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
||||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||||
|
|
||||||
await _organizationService.RemoveUserAsync(orgId, user.Id);
|
await _sut.Leave(orgId);
|
||||||
await _organizationService.Received(1).RemoveUserAsync(orgId, user.Id);
|
|
||||||
|
await _removeOrganizationUserCommand.Received(1).RemoveUserAsync(orgId, user.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, AutoData]
|
[Theory, AutoData]
|
||||||
|
@ -4,8 +4,8 @@ using Bit.Api.AdminConsole.Models.Request.Organizations;
|
|||||||
using Bit.Api.Billing.Controllers;
|
using Bit.Api.Billing.Controllers;
|
||||||
using Bit.Api.Models.Request.Organizations;
|
using Bit.Api.Models.Request.Organizations;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Auth.Entities;
|
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
using Bit.Core.Auth.Models.Data;
|
using Bit.Core.Auth.Models.Data;
|
||||||
using Bit.Core.Auth.Repositories;
|
using Bit.Core.Auth.Repositories;
|
||||||
@ -46,6 +46,7 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
private readonly IAddSecretsManagerSubscriptionCommand _addSecretsManagerSubscriptionCommand;
|
private readonly IAddSecretsManagerSubscriptionCommand _addSecretsManagerSubscriptionCommand;
|
||||||
private readonly IReferenceEventService _referenceEventService;
|
private readonly IReferenceEventService _referenceEventService;
|
||||||
private readonly ISubscriberService _subscriberService;
|
private readonly ISubscriberService _subscriberService;
|
||||||
|
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
||||||
|
|
||||||
private readonly OrganizationsController _sut;
|
private readonly OrganizationsController _sut;
|
||||||
|
|
||||||
@ -68,6 +69,7 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
_addSecretsManagerSubscriptionCommand = Substitute.For<IAddSecretsManagerSubscriptionCommand>();
|
_addSecretsManagerSubscriptionCommand = Substitute.For<IAddSecretsManagerSubscriptionCommand>();
|
||||||
_referenceEventService = Substitute.For<IReferenceEventService>();
|
_referenceEventService = Substitute.For<IReferenceEventService>();
|
||||||
_subscriberService = Substitute.For<ISubscriberService>();
|
_subscriberService = Substitute.For<ISubscriberService>();
|
||||||
|
_removeOrganizationUserCommand = Substitute.For<IRemoveOrganizationUserCommand>();
|
||||||
|
|
||||||
_sut = new OrganizationsController(
|
_sut = new OrganizationsController(
|
||||||
_organizationRepository,
|
_organizationRepository,
|
||||||
@ -91,36 +93,6 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
_sut?.Dispose();
|
_sut?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineAutoData(true, false)]
|
|
||||||
[InlineAutoData(false, true)]
|
|
||||||
[InlineAutoData(false, false)]
|
|
||||||
public async Task OrganizationsController_UserCanLeaveOrganizationThatDoesntProvideKeyConnector(
|
|
||||||
bool keyConnectorEnabled, bool userUsesKeyConnector, Guid orgId, User user)
|
|
||||||
{
|
|
||||||
var ssoConfig = new SsoConfig
|
|
||||||
{
|
|
||||||
Id = default,
|
|
||||||
Data = new SsoConfigurationData
|
|
||||||
{
|
|
||||||
MemberDecryptionType = keyConnectorEnabled
|
|
||||||
? MemberDecryptionType.KeyConnector
|
|
||||||
: MemberDecryptionType.MasterPassword
|
|
||||||
}.Serialize(),
|
|
||||||
Enabled = true,
|
|
||||||
OrganizationId = orgId,
|
|
||||||
};
|
|
||||||
|
|
||||||
user.UsesKeyConnector = userUsesKeyConnector;
|
|
||||||
|
|
||||||
_currentContext.OrganizationUser(orgId).Returns(true);
|
|
||||||
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
|
||||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
|
||||||
|
|
||||||
await _organizationService.RemoveUserAsync(orgId, user.Id);
|
|
||||||
await _organizationService.Received(1).RemoveUserAsync(orgId, user.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, AutoData]
|
[Theory, AutoData]
|
||||||
public async Task OrganizationsController_PostUpgrade_UserCannotEditSubscription_ThrowsNotFoundException(
|
public async Task OrganizationsController_PostUpgrade_UserCannotEditSubscription_ThrowsNotFoundException(
|
||||||
Guid organizationId,
|
Guid organizationId,
|
||||||
|
@ -40,7 +40,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
|
|||||||
Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)))
|
Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)))
|
||||||
.Returns(new Dictionary<Guid, bool> { { organizationUser.Id, true } });
|
.Returns(new Dictionary<Guid, bool> { { organizationUser.Id, true } });
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>()
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
.HasConfirmedOwnersExceptAsync(
|
.HasConfirmedOwnersExceptAsync(
|
||||||
organizationUser.OrganizationId,
|
organizationUser.OrganizationId,
|
||||||
Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)),
|
Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)),
|
||||||
@ -184,7 +184,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
|
|||||||
.OrganizationOwner(organizationUser.OrganizationId)
|
.OrganizationOwner(organizationUser.OrganizationId)
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>()
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
.HasConfirmedOwnersExceptAsync(
|
.HasConfirmedOwnersExceptAsync(
|
||||||
organizationUser.OrganizationId,
|
organizationUser.OrganizationId,
|
||||||
Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)),
|
Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)),
|
||||||
@ -399,8 +399,8 @@ public class DeleteManagedOrganizationUserAccountCommandTests
|
|||||||
.OrganizationOwner(orgUser.OrganizationId)
|
.OrganizationOwner(orgUser.OrganizationId)
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>()
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
.HasConfirmedOwnersExceptAsync(orgUser.OrganizationId, Arg.Any<IEnumerable<Guid>>(), true)
|
.HasConfirmedOwnersExceptAsync(orgUser.OrganizationId, Arg.Any<IEnumerable<Guid>>(), Arg.Any<bool>())
|
||||||
.Returns(false);
|
.Returns(false);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
@ -0,0 +1,139 @@
|
|||||||
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||||
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
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 HasConfirmedOwnersExceptQueryTests
|
||||||
|
{
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task HasConfirmedOwnersExcept_WithConfirmedOwner_WithNoException_ReturnsTrue(
|
||||||
|
Organization organization,
|
||||||
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||||
|
SutProvider<HasConfirmedOwnersExceptQuery> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||||
|
.Returns(new List<OrganizationUser> { owner });
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), true);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task HasConfirmedOwnersExcept_ExcludingConfirmedOwner_ReturnsFalse(
|
||||||
|
Organization organization,
|
||||||
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||||
|
SutProvider<HasConfirmedOwnersExceptQuery> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||||
|
.Returns(new List<OrganizationUser> { owner });
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid> { owner.Id }, true);
|
||||||
|
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task HasConfirmedOwnersExcept_WithInvitedOwner_ReturnsFalse(
|
||||||
|
Organization organization,
|
||||||
|
[OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||||
|
SutProvider<HasConfirmedOwnersExceptQuery> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||||
|
.Returns(new List<OrganizationUser> { owner });
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), true);
|
||||||
|
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(true)]
|
||||||
|
[BitAutoData(false)]
|
||||||
|
public async Task HasConfirmedOwnersExcept_WithConfirmedProviderUser_IncludeProviderTrue_ReturnsTrue(
|
||||||
|
bool includeProvider,
|
||||||
|
Organization organization,
|
||||||
|
ProviderUser providerUser,
|
||||||
|
SutProvider<HasConfirmedOwnersExceptQuery> sutProvider)
|
||||||
|
{
|
||||||
|
providerUser.Status = ProviderUserStatusType.Confirmed;
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IProviderUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organization.Id, ProviderUserStatusType.Confirmed)
|
||||||
|
.Returns(new List<ProviderUser> { providerUser });
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), includeProvider);
|
||||||
|
|
||||||
|
Assert.Equal(includeProvider, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task HasConfirmedOwnersExceptAsync_WithConfirmedOwners_ReturnsTrue(
|
||||||
|
Guid organizationId,
|
||||||
|
IEnumerable<Guid> organizationUsersId,
|
||||||
|
ICollection<OrganizationUser> owners,
|
||||||
|
SutProvider<HasConfirmedOwnersExceptQuery> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
||||||
|
.Returns(owners);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task HasConfirmedOwnersExceptAsync_WithConfirmedProviders_ReturnsTrue(
|
||||||
|
Guid organizationId,
|
||||||
|
IEnumerable<Guid> organizationUsersId,
|
||||||
|
ICollection<ProviderUser> providerUsers,
|
||||||
|
SutProvider<HasConfirmedOwnersExceptQuery> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
||||||
|
.Returns(new List<OrganizationUser>());
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IProviderUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)
|
||||||
|
.Returns(providerUsers);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task HasConfirmedOwnersExceptAsync_WithNoConfirmedOwnersOrProviders_ReturnsFalse(
|
||||||
|
Guid organizationId,
|
||||||
|
IEnumerable<Guid> organizationUsersId,
|
||||||
|
SutProvider<HasConfirmedOwnersExceptQuery> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
||||||
|
.Returns(new List<OrganizationUser>());
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IProviderUserRepository>()
|
||||||
|
.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)
|
||||||
|
.Returns(new List<ProviderUser>());
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
||||||
|
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,12 @@
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Test.AutoFixture.OrganizationUserFixtures;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
@ -14,33 +17,38 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
|||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class RemoveOrganizationUserCommandTests
|
public class RemoveOrganizationUserCommandTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory, BitAutoData]
|
||||||
[BitAutoData]
|
public async Task RemoveUser_Success(
|
||||||
public async Task RemoveUser_Success(SutProvider<RemoveOrganizationUserCommand> sutProvider, Guid organizationId, Guid organizationUserId)
|
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser deletingUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
.GetByIdAsync(organizationUserId)
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||||
.Returns(new OrganizationUser
|
|
||||||
{
|
|
||||||
Id = organizationUserId,
|
|
||||||
OrganizationId = organizationId
|
|
||||||
});
|
|
||||||
|
|
||||||
await sutProvider.Sut.RemoveUserAsync(organizationId, organizationUserId, null);
|
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
||||||
|
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
||||||
|
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
||||||
|
currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).RemoveUserAsync(organizationId, organizationUserId, null);
|
await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
|
||||||
|
|
||||||
|
await organizationUserRepository.Received(1).DeleteAsync(organizationUser);
|
||||||
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task RemoveUser_NotFound_Throws(SutProvider<RemoveOrganizationUserCommand> sutProvider, Guid organizationId, Guid organizationUserId)
|
public async Task RemoveUser_NotFound_ThrowsException(SutProvider<RemoveOrganizationUserCommand> sutProvider,
|
||||||
|
Guid organizationId, Guid organizationUserId)
|
||||||
{
|
{
|
||||||
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.RemoveUserAsync(organizationId, organizationUserId, null));
|
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.RemoveUserAsync(organizationId, organizationUserId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task RemoveUser_MismatchingOrganizationId_Throws(SutProvider<RemoveOrganizationUserCommand> sutProvider, Guid organizationId, Guid organizationUserId)
|
public async Task RemoveUser_MismatchingOrganizationId_ThrowsException(
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider, Guid organizationId, Guid organizationUserId)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
.GetByIdAsync(organizationUserId)
|
.GetByIdAsync(organizationUserId)
|
||||||
@ -53,20 +61,249 @@ public class RemoveOrganizationUserCommandTests
|
|||||||
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.RemoveUserAsync(organizationId, organizationUserId, null));
|
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.RemoveUserAsync(organizationId, organizationUserId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory, BitAutoData]
|
||||||
[BitAutoData]
|
public async Task RemoveUser_InvalidUser_ThrowsException(
|
||||||
public async Task RemoveUser_WithEventSystemUser_Success(SutProvider<RemoveOrganizationUserCommand> sutProvider, Guid organizationId, Guid organizationUserId, EventSystemUser eventSystemUser)
|
OrganizationUser organizationUser, OrganizationUser deletingUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
.GetByIdAsync(organizationUserId)
|
|
||||||
.Returns(new OrganizationUser
|
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<NotFoundException>(
|
||||||
|
() => sutProvider.Sut.RemoveUserAsync(Guid.NewGuid(), organizationUser.Id, deletingUser.UserId));
|
||||||
|
Assert.Contains("User not found.", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUser_RemoveYourself_ThrowsException(OrganizationUser deletingUser, SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
{
|
{
|
||||||
Id = organizationUserId,
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
OrganizationId = organizationId
|
|
||||||
});
|
|
||||||
|
|
||||||
await sutProvider.Sut.RemoveUserAsync(organizationId, organizationUserId, eventSystemUser);
|
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).RemoveUserAsync(organizationId, organizationUserId, eventSystemUser);
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, deletingUser.Id, deletingUser.UserId));
|
||||||
|
Assert.Contains("You cannot remove yourself.", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUser_NonOwnerRemoveOwner_ThrowsException(
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser deletingUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||||
|
|
||||||
|
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
||||||
|
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
||||||
|
currentContext.OrganizationAdmin(deletingUser.OrganizationId).Returns(true);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId));
|
||||||
|
Assert.Contains("Only owners can delete other owners.", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUser_RemovingLastOwner_ThrowsException(
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser,
|
||||||
|
OrganizationUser deletingUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
var hasConfirmedOwnersExceptQuery = sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>();
|
||||||
|
|
||||||
|
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
||||||
|
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
||||||
|
hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(
|
||||||
|
deletingUser.OrganizationId,
|
||||||
|
Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.Id)), Arg.Any<bool>())
|
||||||
|
.Returns(false);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, null));
|
||||||
|
Assert.Contains("Organization must have at least one confirmed owner.", exception.Message);
|
||||||
|
hasConfirmedOwnersExceptQuery
|
||||||
|
.Received(1)
|
||||||
|
.HasConfirmedOwnersExceptAsync(
|
||||||
|
organizationUser.OrganizationId,
|
||||||
|
Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.Id)), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUser_WithEventSystemUser_Success(
|
||||||
|
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser,
|
||||||
|
EventSystemUser eventSystemUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
|
||||||
|
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
||||||
|
|
||||||
|
await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.Id, eventSystemUser);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUser_ByUserId_Success(
|
||||||
|
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
|
||||||
|
organizationUserRepository
|
||||||
|
.GetByOrganizationAsync(organizationUser.OrganizationId, organizationUser.UserId!.Value)
|
||||||
|
.Returns(organizationUser);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
|
.HasConfirmedOwnersExceptAsync(
|
||||||
|
organizationUser.OrganizationId,
|
||||||
|
Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.Id)),
|
||||||
|
Arg.Any<bool>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.UserId.Value);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUser_ByUserId_NotFound_ThrowsException(SutProvider<RemoveOrganizationUserCommand> sutProvider,
|
||||||
|
Guid organizationId, Guid userId)
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.RemoveUserAsync(organizationId, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUser_ByUserId_InvalidUser_ThrowsException(OrganizationUser organizationUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
|
||||||
|
organizationUserRepository.GetByOrganizationAsync(organizationUser.OrganizationId, organizationUser.UserId!.Value).Returns(organizationUser);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<NotFoundException>(
|
||||||
|
() => sutProvider.Sut.RemoveUserAsync(Guid.NewGuid(), organizationUser.UserId.Value));
|
||||||
|
Assert.Contains("User not found.", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUser_ByUserId_RemovingLastOwner_ThrowsException(
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
var hasConfirmedOwnersExceptQuery = sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>();
|
||||||
|
|
||||||
|
organizationUserRepository.GetByOrganizationAsync(organizationUser.OrganizationId, organizationUser.UserId!.Value).Returns(organizationUser);
|
||||||
|
hasConfirmedOwnersExceptQuery
|
||||||
|
.HasConfirmedOwnersExceptAsync(
|
||||||
|
organizationUser.OrganizationId,
|
||||||
|
Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.Id)),
|
||||||
|
Arg.Any<bool>())
|
||||||
|
.Returns(false);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.UserId.Value));
|
||||||
|
Assert.Contains("Organization must have at least one confirmed owner.", exception.Message);
|
||||||
|
hasConfirmedOwnersExceptQuery
|
||||||
|
.Received(1)
|
||||||
|
.HasConfirmedOwnersExceptAsync(
|
||||||
|
organizationUser.OrganizationId,
|
||||||
|
Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.Id)),
|
||||||
|
Arg.Any<bool>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUsers_FilterInvalid_ThrowsException(OrganizationUser organizationUser, OrganizationUser deletingUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
var organizationUsers = new[] { organizationUser };
|
||||||
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
||||||
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId));
|
||||||
|
Assert.Contains("Users invalid.", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUsers_RemoveYourself_ThrowsException(
|
||||||
|
OrganizationUser deletingUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
var organizationUsers = new[] { deletingUser };
|
||||||
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
||||||
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
||||||
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
|
.HasConfirmedOwnersExceptAsync(deletingUser.OrganizationId, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
||||||
|
Assert.Contains("You cannot remove yourself.", result[0].Item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUsers_NonOwnerRemoveOwner_ThrowsException(
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser deletingUser,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1,
|
||||||
|
[OrganizationUser(OrganizationUserStatusType.Confirmed)] OrganizationUser orgUser2,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
|
||||||
|
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
||||||
|
var organizationUsers = new[] { orgUser1 };
|
||||||
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
||||||
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
||||||
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
|
.HasConfirmedOwnersExceptAsync(deletingUser.OrganizationId, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
||||||
|
Assert.Contains("Only owners can delete other owners.", result[0].Item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUsers_LastOwner_ThrowsException(
|
||||||
|
[OrganizationUser(status: OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
|
||||||
|
var organizationUsers = new[] { orgUser };
|
||||||
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
||||||
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
||||||
|
organizationUserRepository.GetManyByOrganizationAsync(orgUser.OrganizationId, OrganizationUserType.Owner).Returns(organizationUsers);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.RemoveUsersAsync(orgUser.OrganizationId, organizationUserIds, null));
|
||||||
|
Assert.Contains("Organization must have at least one confirmed owner.", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveUsers_Success(
|
||||||
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser,
|
||||||
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1, OrganizationUser orgUser2,
|
||||||
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||||
|
|
||||||
|
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
||||||
|
var organizationUsers = new[] { orgUser1, orgUser2 };
|
||||||
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
||||||
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
||||||
|
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
||||||
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
|
.HasConfirmedOwnersExceptAsync(deletingUser.OrganizationId, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true);
|
||||||
|
|
||||||
|
await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -169,7 +170,7 @@ public class UpdateOrganizationUserCommandTests
|
|||||||
await organizationService.Received(1).ValidateOrganizationCustomPermissionsEnabledAsync(
|
await organizationService.Received(1).ValidateOrganizationCustomPermissionsEnabledAsync(
|
||||||
newUserData.OrganizationId,
|
newUserData.OrganizationId,
|
||||||
newUserData.Type);
|
newUserData.Type);
|
||||||
await organizationService.Received(1).HasConfirmedOwnersExceptAsync(
|
await sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>().Received(1).HasConfirmedOwnersExceptAsync(
|
||||||
newUserData.OrganizationId,
|
newUserData.OrganizationId,
|
||||||
Arg.Is<IEnumerable<Guid>>(i => i.Contains(newUserData.Id)));
|
Arg.Is<IEnumerable<Guid>>(i => i.Contains(newUserData.Id)));
|
||||||
}
|
}
|
||||||
@ -187,7 +188,7 @@ public class UpdateOrganizationUserCommandTests
|
|||||||
newUser.UserId = oldUser.UserId;
|
newUser.UserId = oldUser.UserId;
|
||||||
newUser.OrganizationId = oldUser.OrganizationId = organization.Id;
|
newUser.OrganizationId = oldUser.OrganizationId = organization.Id;
|
||||||
organizationUserRepository.GetByIdAsync(oldUser.Id).Returns(oldUser);
|
organizationUserRepository.GetByIdAsync(oldUser.Id).Returns(oldUser);
|
||||||
organizationService
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
.HasConfirmedOwnersExceptAsync(
|
.HasConfirmedOwnersExceptAsync(
|
||||||
oldUser.OrganizationId,
|
oldUser.OrganizationId,
|
||||||
Arg.Is<IEnumerable<Guid>>(i => i.Contains(oldUser.Id)))
|
Arg.Is<IEnumerable<Guid>>(i => i.Contains(oldUser.Id)))
|
||||||
|
@ -43,6 +43,8 @@ using Xunit;
|
|||||||
using Organization = Bit.Core.AdminConsole.Entities.Organization;
|
using Organization = Bit.Core.AdminConsole.Entities.Organization;
|
||||||
using OrganizationUser = Bit.Core.Entities.OrganizationUser;
|
using OrganizationUser = Bit.Core.Entities.OrganizationUser;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
namespace Bit.Core.Test.Services;
|
namespace Bit.Core.Test.Services;
|
||||||
|
|
||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
@ -77,8 +79,9 @@ public class OrganizationServiceTests
|
|||||||
.Returns(existingUsers);
|
.Returns(existingUsers);
|
||||||
organizationUserRepository.GetCountByOrganizationIdAsync(org.Id)
|
organizationUserRepository.GetCountByOrganizationIdAsync(org.Id)
|
||||||
.Returns(existingUsers.Count);
|
.Returns(existingUsers.Count);
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(org.Id, OrganizationUserType.Owner)
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
.Returns(existingUsers.Select(u => new OrganizationUser { Status = OrganizationUserStatusType.Confirmed, Type = OrganizationUserType.Owner, Id = u.Id }).ToList());
|
.HasConfirmedOwnersExceptAsync(org.Id, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(org.Id).Returns(true);
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(org.Id).Returns(true);
|
||||||
|
|
||||||
// Mock tokenable factory to return a token that expires in 5 days
|
// Mock tokenable factory to return a token that expires in 5 days
|
||||||
@ -147,8 +150,9 @@ public class OrganizationServiceTests
|
|||||||
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(org.Id, OrganizationUserType.Owner)
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
.Returns(existingUsers.Select(u => new OrganizationUser { Status = OrganizationUserStatusType.Confirmed, Type = OrganizationUserType.Owner, Id = u.Id }).ToList());
|
.HasConfirmedOwnersExceptAsync(org.Id, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||||
|
|
||||||
@ -708,8 +712,9 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
.Returns(new[] { invitor });
|
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||||
|
|
||||||
@ -737,8 +742,9 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
.Returns(new[] { invitor });
|
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||||
|
|
||||||
@ -817,8 +823,9 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
.Returns(new[] { invitor });
|
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||||
|
|
||||||
@ -835,7 +842,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
), OrganizationCustomize, BitAutoData]
|
), OrganizationCustomize, BitAutoData]
|
||||||
public async Task InviteUser_Passes(Organization organization, OrganizationUserInvite invite, string externalId,
|
public async Task InviteUser_Passes(Organization organization, OrganizationUserInvite invite, string externalId,
|
||||||
OrganizationUser invitor,
|
OrganizationUser invitor,
|
||||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
// This method is only used to invite 1 user at a time
|
// This method is only used to invite 1 user at a time
|
||||||
@ -851,8 +857,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
||||||
.Returns(new[] { owner });
|
|
||||||
|
|
||||||
// Mock tokenable factory to return a token that expires in 5 days
|
// Mock tokenable factory to return a token that expires in 5 days
|
||||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||||
@ -864,6 +868,10 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
|
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||||
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
||||||
|
|
||||||
@ -905,7 +913,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
), OrganizationCustomize, BitAutoData]
|
), OrganizationCustomize, BitAutoData]
|
||||||
public async Task InviteUser_UserAlreadyInvited_Throws(Organization organization, OrganizationUserInvite invite, string externalId,
|
public async Task InviteUser_UserAlreadyInvited_Throws(Organization organization, OrganizationUserInvite invite, string externalId,
|
||||||
OrganizationUser invitor,
|
OrganizationUser invitor,
|
||||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
// This method is only used to invite 1 user at a time
|
// This method is only used to invite 1 user at a time
|
||||||
@ -926,8 +933,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
||||||
.Returns(new[] { owner });
|
|
||||||
|
|
||||||
// Mock tokenable factory to return a token that expires in 5 days
|
// Mock tokenable factory to return a token that expires in 5 days
|
||||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||||
@ -939,6 +944,10 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
|
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||||
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
||||||
|
|
||||||
@ -987,7 +996,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
), OrganizationCustomize, BitAutoData]
|
), OrganizationCustomize, BitAutoData]
|
||||||
public async Task InviteUsers_Passes(Organization organization, IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
|
public async Task InviteUsers_Passes(Organization organization, IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
|
||||||
OrganizationUser invitor,
|
OrganizationUser invitor,
|
||||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
||||||
@ -1000,8 +1008,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
||||||
.Returns(new[] { owner });
|
|
||||||
|
|
||||||
// Mock tokenable factory to return a token that expires in 5 days
|
// Mock tokenable factory to return a token that expires in 5 days
|
||||||
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
||||||
@ -1013,6 +1019,10 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
|
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||||
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
||||||
|
|
||||||
@ -1034,7 +1044,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
), OrganizationCustomize, BitAutoData]
|
), OrganizationCustomize, BitAutoData]
|
||||||
public async Task InviteUsers_WithEventSystemUser_Passes(Organization organization, EventSystemUser eventSystemUser, IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
|
public async Task InviteUsers_WithEventSystemUser_Passes(Organization organization, EventSystemUser eventSystemUser, IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
|
||||||
OrganizationUser invitor,
|
OrganizationUser invitor,
|
||||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
||||||
@ -1052,8 +1061,9 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||||
|
|
||||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
.Returns(new[] { owner });
|
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
||||||
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
||||||
@ -1181,196 +1191,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUser_InvalidUser(OrganizationUser organizationUser, OrganizationUser deletingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
|
|
||||||
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.RemoveUserAsync(Guid.NewGuid(), organizationUser.Id, deletingUser.UserId));
|
|
||||||
Assert.Contains("User not valid.", exception.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUser_RemoveYourself(OrganizationUser deletingUser, SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
|
|
||||||
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, deletingUser.Id, deletingUser.UserId));
|
|
||||||
Assert.Contains("You cannot remove yourself.", exception.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUser_NonOwnerRemoveOwner(
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser deletingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
|
||||||
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
|
||||||
currentContext.OrganizationAdmin(deletingUser.OrganizationId).Returns(true);
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId));
|
|
||||||
Assert.Contains("Only owners can delete other owners.", exception.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUser_LastOwner(
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser,
|
|
||||||
OrganizationUser deletingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
|
|
||||||
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
|
||||||
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(deletingUser.OrganizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new[] { organizationUser });
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, null));
|
|
||||||
Assert.Contains("Organization must have at least one confirmed owner.", exception.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUser_Success(
|
|
||||||
OrganizationUser organizationUser,
|
|
||||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
|
||||||
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
|
||||||
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(deletingUser.OrganizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new[] { deletingUser, organizationUser });
|
|
||||||
currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true);
|
|
||||||
|
|
||||||
await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUser_WithEventSystemUser_Success(
|
|
||||||
OrganizationUser organizationUser,
|
|
||||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser, EventSystemUser eventSystemUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
|
||||||
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
|
||||||
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(deletingUser.OrganizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new[] { deletingUser, organizationUser });
|
|
||||||
currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true);
|
|
||||||
|
|
||||||
await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, eventSystemUser);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUsers_FilterInvalid(OrganizationUser organizationUser, OrganizationUser deletingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var organizationUsers = new[] { organizationUser };
|
|
||||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
||||||
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId));
|
|
||||||
Assert.Contains("Users invalid.", exception.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUsers_RemoveYourself(
|
|
||||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser,
|
|
||||||
OrganizationUser deletingUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var organizationUsers = new[] { deletingUser };
|
|
||||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
||||||
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(default, default).ReturnsForAnyArgs(new[] { orgUser });
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
|
||||||
Assert.Contains("You cannot remove yourself.", result[0].Item2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUsers_NonOwnerRemoveOwner(
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser deletingUser,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1,
|
|
||||||
[OrganizationUser(OrganizationUserStatusType.Confirmed)] OrganizationUser orgUser2,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
|
|
||||||
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
|
||||||
var organizationUsers = new[] { orgUser1 };
|
|
||||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
||||||
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(default, default).ReturnsForAnyArgs(new[] { orgUser2 });
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
|
||||||
Assert.Contains("Only owners can delete other owners.", result[0].Item2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUsers_LastOwner(
|
|
||||||
[OrganizationUser(status: OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
|
|
||||||
var organizationUsers = new[] { orgUser };
|
|
||||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
||||||
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(orgUser.OrganizationId, OrganizationUserType.Owner).Returns(organizationUsers);
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.RemoveUsersAsync(orgUser.OrganizationId, organizationUserIds, null));
|
|
||||||
Assert.Contains("Organization must have at least one confirmed owner.", exception.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUsers_Success(
|
|
||||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1, OrganizationUser orgUser2,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
||||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
||||||
|
|
||||||
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
|
||||||
var organizationUsers = new[] { orgUser1, orgUser2 };
|
|
||||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
||||||
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
|
||||||
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(deletingUser.OrganizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new[] { deletingUser, orgUser1 });
|
|
||||||
currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true);
|
|
||||||
|
|
||||||
await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task ConfirmUser_InvalidStatus(OrganizationUser confirmingUser,
|
public async Task ConfirmUser_InvalidStatus(OrganizationUser confirmingUser,
|
||||||
[OrganizationUser(OrganizationUserStatusType.Invited)] OrganizationUser orgUser, string key,
|
[OrganizationUser(OrganizationUserStatusType.Invited)] OrganizationUser orgUser, string key,
|
||||||
@ -1858,17 +1678,24 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
await applicationCacheService.DidNotReceiveWithAnyArgs().DeleteOrganizationAbilityAsync(default);
|
await applicationCacheService.DidNotReceiveWithAnyArgs().DeleteOrganizationAbilityAsync(default);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RestoreRevokeUser_Setup(Organization organization, OrganizationUser restoringUser,
|
private void RestoreRevokeUser_Setup(
|
||||||
OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider,
|
Organization organization,
|
||||||
OrganizationUserType restoringUserType = OrganizationUserType.Owner)
|
OrganizationUser? requestingOrganizationUser,
|
||||||
|
OrganizationUser targetOrganizationUser,
|
||||||
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
|
if (requestingOrganizationUser != null)
|
||||||
|
{
|
||||||
|
requestingOrganizationUser.OrganizationId = organization.Id;
|
||||||
|
}
|
||||||
|
targetOrganizationUser.OrganizationId = organization.Id;
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationUser.OrganizationId).Returns(organization);
|
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(requestingOrganizationUser != null && requestingOrganizationUser.Type is OrganizationUserType.Owner);
|
||||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(requestingOrganizationUser != null && (requestingOrganizationUser.Type is OrganizationUserType.Owner or OrganizationUserType.Admin));
|
||||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>())
|
||||||
organizationUserRepository.GetManyByOrganizationAsync(organizationUser.OrganizationId, restoringUserType)
|
.Returns(true);
|
||||||
.Returns(new[] { restoringUser });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@ -1887,10 +1714,9 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task RevokeUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
public async Task RevokeUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
|
||||||
[OrganizationUser] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
{
|
||||||
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
RestoreRevokeUser_Setup(organization, null, organizationUser, sutProvider);
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
var eventService = sutProvider.GetDependency<IEventService>();
|
var eventService = sutProvider.GetDependency<IEventService>();
|
||||||
|
|
||||||
@ -1917,10 +1743,9 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task RestoreUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
public async Task RestoreUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
|
||||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
{
|
||||||
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
RestoreRevokeUser_Setup(organization, null, organizationUser, sutProvider);
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
var eventService = sutProvider.GetDependency<IEventService>();
|
var eventService = sutProvider.GetDependency<IEventService>();
|
||||||
|
|
||||||
@ -1953,11 +1778,12 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(OrganizationUserType.Admin)]
|
[BitAutoData(OrganizationUserType.Admin)]
|
||||||
[BitAutoData(OrganizationUserType.Custom)]
|
[BitAutoData(OrganizationUserType.Custom)]
|
||||||
public async Task RestoreUser_AdminRestoreOwner_Fails(OrganizationUserType restoringUserType, Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed)] OrganizationUser restoringUser,
|
public async Task RestoreUser_AdminRestoreOwner_Fails(OrganizationUserType restoringUserType,
|
||||||
|
Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed)] OrganizationUser restoringUser,
|
||||||
[OrganizationUser(OrganizationUserStatusType.Revoked, OrganizationUserType.Owner)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
|
[OrganizationUser(OrganizationUserStatusType.Revoked, OrganizationUserType.Owner)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
restoringUser.Type = restoringUserType;
|
restoringUser.Type = restoringUserType;
|
||||||
RestoreRevokeUser_Setup(organization, restoringUser, organizationUser, sutProvider, OrganizationUserType.Admin);
|
RestoreRevokeUser_Setup(organization, restoringUser, organizationUser, sutProvider);
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
var eventService = sutProvider.GetDependency<IEventService>();
|
var eventService = sutProvider.GetDependency<IEventService>();
|
||||||
|
|
||||||
@ -2028,9 +1854,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
organizationUser.Email = null;
|
organizationUser.Email = null;
|
||||||
sutProvider.GetDependency<IPolicyService>()
|
|
||||||
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
|
|
||||||
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } });
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
|
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
|
||||||
@ -2040,6 +1863,10 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
var eventService = sutProvider.GetDependency<IEventService>();
|
var eventService = sutProvider.GetDependency<IEventService>();
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IPolicyService>()
|
||||||
|
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
|
||||||
|
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } });
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
|
||||||
|
|
||||||
@ -2152,14 +1979,15 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
organizationUser.Email = null;
|
organizationUser.Email = null;
|
||||||
sutProvider.GetDependency<IPolicyService>()
|
|
||||||
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
|
|
||||||
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } });
|
|
||||||
|
|
||||||
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
||||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||||
var eventService = sutProvider.GetDependency<IEventService>();
|
var eventService = sutProvider.GetDependency<IEventService>();
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IPolicyService>()
|
||||||
|
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
|
||||||
|
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } });
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
|
||||||
|
|
||||||
@ -2199,58 +2027,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task HasConfirmedOwnersExcept_WithConfirmedOwner_ReturnsTrue(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser> { owner });
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), true);
|
|
||||||
|
|
||||||
Assert.True(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task HasConfirmedOwnersExcept_ExcludingConfirmedOwner_ReturnsFalse(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser> { owner });
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid> { owner.Id }, true);
|
|
||||||
|
|
||||||
Assert.False(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task HasConfirmedOwnersExcept_WithInvitedOwner_ReturnsFalse(Organization organization, [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.Owner)] OrganizationUser owner, SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser> { owner });
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), true);
|
|
||||||
|
|
||||||
Assert.False(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(true)]
|
|
||||||
[BitAutoData(false)]
|
|
||||||
public async Task HasConfirmedOwnersExcept_WithConfirmedProviderUser_IncludeProviderTrue_ReturnsTrue(bool includeProvider, Organization organization, ProviderUser providerUser, SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
providerUser.Status = ProviderUserStatusType.Confirmed;
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IProviderUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organization.Id, ProviderUserStatusType.Confirmed)
|
|
||||||
.Returns(new List<ProviderUser> { providerUser });
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), includeProvider);
|
|
||||||
|
|
||||||
Assert.Equal(includeProvider, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(PlanType.TeamsAnnually)]
|
[BitAutoData(PlanType.TeamsAnnually)]
|
||||||
[BitAutoData(PlanType.TeamsMonthly)]
|
[BitAutoData(PlanType.TeamsMonthly)]
|
||||||
@ -2474,61 +2250,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
Assert.Contains("custom users can only grant the same custom permissions that they have.", exception.Message.ToLowerInvariant());
|
Assert.Contains("custom users can only grant the same custom permissions that they have.", exception.Message.ToLowerInvariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task HasConfirmedOwnersExceptAsync_WithConfirmedOwners_ReturnsTrue(
|
|
||||||
Guid organizationId,
|
|
||||||
IEnumerable<Guid> organizationUsersId,
|
|
||||||
ICollection<OrganizationUser> owners,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(owners);
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
|
||||||
|
|
||||||
Assert.True(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task HasConfirmedOwnersExceptAsync_WithConfirmedProviders_ReturnsTrue(
|
|
||||||
Guid organizationId,
|
|
||||||
IEnumerable<Guid> organizationUsersId,
|
|
||||||
ICollection<ProviderUser> providerUsers,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser>());
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IProviderUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)
|
|
||||||
.Returns(providerUsers);
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
|
||||||
|
|
||||||
Assert.True(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task HasConfirmedOwnersExceptAsync_WithNoConfirmedOwnersOrProviders_ReturnsFalse(
|
|
||||||
Guid organizationId,
|
|
||||||
IEnumerable<Guid> organizationUsersId,
|
|
||||||
SutProvider<OrganizationService> sutProvider)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
|
||||||
.Returns(new List<OrganizationUser>());
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IProviderUserRepository>()
|
|
||||||
.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)
|
|
||||||
.Returns(new List<ProviderUser>());
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
|
||||||
|
|
||||||
Assert.False(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(OrganizationUserType.Owner)]
|
[BitAutoData(OrganizationUserType.Owner)]
|
||||||
[BitAutoData(OrganizationUserType.Admin)]
|
[BitAutoData(OrganizationUserType.Admin)]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Enums;
|
using Bit.Core.AdminConsole.Enums;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services.Implementations;
|
using Bit.Core.AdminConsole.Services.Implementations;
|
||||||
using Bit.Core.Auth.Entities;
|
using Bit.Core.Auth.Entities;
|
||||||
@ -353,6 +354,7 @@ public class PolicyServiceTests
|
|||||||
});
|
});
|
||||||
|
|
||||||
var organizationService = Substitute.For<IOrganizationService>();
|
var organizationService = Substitute.For<IOrganizationService>();
|
||||||
|
var removeOrganizationUserCommand = sutProvider.GetDependency<IRemoveOrganizationUserCommand>();
|
||||||
|
|
||||||
var utcNow = DateTime.UtcNow;
|
var utcNow = DateTime.UtcNow;
|
||||||
|
|
||||||
@ -360,20 +362,20 @@ public class PolicyServiceTests
|
|||||||
|
|
||||||
await sutProvider.Sut.SaveAsync(policy, organizationService, savingUserId);
|
await sutProvider.Sut.SaveAsync(policy, organizationService, savingUserId);
|
||||||
|
|
||||||
await organizationService.Received()
|
await removeOrganizationUserCommand.Received()
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWithout2FA.Id, savingUserId);
|
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWithout2FA.Id, savingUserId);
|
||||||
await sutProvider.GetDependency<IMailService>().Received()
|
await sutProvider.GetDependency<IMailService>().Received()
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWithout2FA.Email);
|
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWithout2FA.Email);
|
||||||
|
|
||||||
await organizationService.DidNotReceive()
|
await removeOrganizationUserCommand.DidNotReceive()
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserInvited.Id, savingUserId);
|
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserInvited.Id, savingUserId);
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserInvited.Email);
|
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserInvited.Email);
|
||||||
await organizationService.DidNotReceive()
|
await removeOrganizationUserCommand.DidNotReceive()
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWith2FA.Id, savingUserId);
|
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWith2FA.Id, savingUserId);
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWith2FA.Email);
|
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWith2FA.Email);
|
||||||
await organizationService.DidNotReceive()
|
await removeOrganizationUserCommand.DidNotReceive()
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailAdmin.Id, savingUserId);
|
.RemoveUserAsync(policy.OrganizationId, orgUserDetailAdmin.Id, savingUserId);
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailAdmin.Email);
|
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailAdmin.Email);
|
||||||
@ -467,6 +469,7 @@ public class PolicyServiceTests
|
|||||||
});
|
});
|
||||||
|
|
||||||
var organizationService = Substitute.For<IOrganizationService>();
|
var organizationService = Substitute.For<IOrganizationService>();
|
||||||
|
var removeOrganizationUserCommand = sutProvider.GetDependency<IRemoveOrganizationUserCommand>();
|
||||||
|
|
||||||
var savingUserId = Guid.NewGuid();
|
var savingUserId = Guid.NewGuid();
|
||||||
|
|
||||||
@ -475,7 +478,7 @@ public class PolicyServiceTests
|
|||||||
|
|
||||||
Assert.Contains("Policy could not be enabled. Non-compliant members will lose access to their accounts. Identify members without two-step login from the policies column in the members page.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("Policy could not be enabled. Non-compliant members will lose access to their accounts. Identify members without two-step login from the policies column in the members page.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
await organizationService.DidNotReceiveWithAnyArgs()
|
await removeOrganizationUserCommand.DidNotReceiveWithAnyArgs()
|
||||||
.RemoveUserAsync(organizationId: default, organizationUserId: default, deletingUserId: default);
|
.RemoveUserAsync(organizationId: default, organizationUserId: default, deletingUserId: default);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs()
|
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
@ -263,7 +264,8 @@ public class UserServiceTests
|
|||||||
sutProvider.GetDependency<IStripeSyncService>(),
|
sutProvider.GetDependency<IStripeSyncService>(),
|
||||||
new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>(),
|
new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>(),
|
||||||
sutProvider.GetDependency<IFeatureService>(),
|
sutProvider.GetDependency<IFeatureService>(),
|
||||||
sutProvider.GetDependency<IPremiumUserBillingService>()
|
sutProvider.GetDependency<IPremiumUserBillingService>(),
|
||||||
|
sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
|
||||||
);
|
);
|
||||||
|
|
||||||
var actualIsVerified = await sut.VerifySecretAsync(user, secret);
|
var actualIsVerified = await sut.VerifySecretAsync(user, secret);
|
||||||
|
Loading…
Reference in New Issue
Block a user