mirror of
https://github.com/bitwarden/server.git
synced 2025-05-14 08:02:17 -05:00
[PM-14613] Remove account deprovisioning feature flag (#5676)
* Remove flag * Remove old tests * Remove old xmldoc referencing the flag * Remove old emails
This commit is contained in:
parent
952967b8b3
commit
a1b22e66e5
@ -4,7 +4,6 @@ using Bit.Admin.Enums;
|
|||||||
using Bit.Admin.Models;
|
using Bit.Admin.Models;
|
||||||
using Bit.Admin.Services;
|
using Bit.Admin.Services;
|
||||||
using Bit.Admin.Utilities;
|
using Bit.Admin.Utilities;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -89,7 +88,7 @@ public class UsersController : Controller
|
|||||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
|
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
|
||||||
|
|
||||||
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||||
var verifiedDomain = await AccountDeprovisioningEnabled(user.Id);
|
var verifiedDomain = await _userService.IsClaimedByAnyOrganizationAsync(user.Id);
|
||||||
return View(UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers, verifiedDomain));
|
return View(UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers, verifiedDomain));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +105,7 @@ public class UsersController : Controller
|
|||||||
var billingInfo = await _paymentService.GetBillingAsync(user);
|
var billingInfo = await _paymentService.GetBillingAsync(user);
|
||||||
var billingHistoryInfo = await _paymentService.GetBillingHistoryAsync(user);
|
var billingHistoryInfo = await _paymentService.GetBillingHistoryAsync(user);
|
||||||
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||||
var verifiedDomain = await AccountDeprovisioningEnabled(user.Id);
|
var verifiedDomain = await _userService.IsClaimedByAnyOrganizationAsync(user.Id);
|
||||||
var deviceVerificationRequired = await _userService.ActiveNewDeviceVerificationException(user.Id);
|
var deviceVerificationRequired = await _userService.ActiveNewDeviceVerificationException(user.Id);
|
||||||
|
|
||||||
return View(new UserEditModel(user, isTwoFactorEnabled, ciphers, billingInfo, billingHistoryInfo, _globalSettings, verifiedDomain, deviceVerificationRequired));
|
return View(new UserEditModel(user, isTwoFactorEnabled, ciphers, billingInfo, billingHistoryInfo, _globalSettings, verifiedDomain, deviceVerificationRequired));
|
||||||
@ -178,12 +177,4 @@ public class UsersController : Controller
|
|||||||
await _userService.ToggleNewDeviceVerificationException(user.Id);
|
await _userService.ToggleNewDeviceVerificationException(user.Id);
|
||||||
return RedirectToAction("Edit", new { id });
|
return RedirectToAction("Edit", new { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Feature flag to be removed in PM-14207
|
|
||||||
private async Task<bool?> AccountDeprovisioningEnabled(Guid userId)
|
|
||||||
{
|
|
||||||
return _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
? await _userService.IsClaimedByAnyOrganizationAsync(userId)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -616,7 +616,6 @@ public class OrganizationUsersController : Controller
|
|||||||
new OrganizationUserBulkResponseModel(r.OrganizationUserId, r.ErrorMessage)));
|
new OrganizationUserBulkResponseModel(r.OrganizationUserId, r.ErrorMessage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[RequireFeature(FeatureFlagKeys.AccountDeprovisioning)]
|
|
||||||
[HttpDelete("{id}/delete-account")]
|
[HttpDelete("{id}/delete-account")]
|
||||||
[HttpPost("{id}/delete-account")]
|
[HttpPost("{id}/delete-account")]
|
||||||
public async Task DeleteAccount(Guid orgId, Guid id)
|
public async Task DeleteAccount(Guid orgId, Guid id)
|
||||||
@ -635,7 +634,6 @@ public class OrganizationUsersController : Controller
|
|||||||
await _deleteClaimedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUser.Id);
|
await _deleteClaimedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUser.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RequireFeature(FeatureFlagKeys.AccountDeprovisioning)]
|
|
||||||
[HttpDelete("delete-account")]
|
[HttpDelete("delete-account")]
|
||||||
[HttpPost("delete-account")]
|
[HttpPost("delete-account")]
|
||||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDeleteAccount(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDeleteAccount(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||||
@ -760,11 +758,6 @@ public class OrganizationUsersController : Controller
|
|||||||
|
|
||||||
private async Task<IDictionary<Guid, bool>> GetClaimedByOrganizationStatusAsync(Guid orgId, IEnumerable<Guid> userIds)
|
private async Task<IDictionary<Guid, bool>> GetClaimedByOrganizationStatusAsync(Guid orgId, IEnumerable<Guid> userIds)
|
||||||
{
|
{
|
||||||
if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
|
||||||
{
|
|
||||||
return userIds.ToDictionary(kvp => kvp, kvp => false);
|
|
||||||
}
|
|
||||||
|
|
||||||
var usersOrganizationClaimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(orgId, userIds);
|
var usersOrganizationClaimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(orgId, userIds);
|
||||||
return usersOrganizationClaimedStatus;
|
return usersOrganizationClaimedStatus;
|
||||||
}
|
}
|
||||||
|
@ -279,8 +279,7 @@ public class OrganizationsController : Controller
|
|||||||
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
if ((await _userService.GetOrganizationsClaimingUserAsync(user.Id)).Any(x => x.Id == id))
|
||||||
&& (await _userService.GetOrganizationsClaimingUserAsync(user.Id)).Any(x => x.Id == id))
|
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Claimed user account cannot leave claiming organization. Contact your organization administrator for additional details.");
|
throw new BadRequestException("Claimed user account cannot leave claiming organization. Contact your organization administrator for additional details.");
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
using Bit.Api.AdminConsole.Models.Response.Helpers;
|
using Bit.Api.AdminConsole.Models.Response.Helpers;
|
||||||
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Enums;
|
using Bit.Core.AdminConsole.Enums;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
||||||
@ -79,7 +78,7 @@ public class PoliciesController : Controller
|
|||||||
return new PolicyDetailResponseModel(new Policy { Type = (PolicyType)type });
|
return new PolicyDetailResponseModel(new Policy { Type = (PolicyType)type });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) && policy.Type is PolicyType.SingleOrg)
|
if (policy.Type is PolicyType.SingleOrg)
|
||||||
{
|
{
|
||||||
return await policy.GetSingleOrgPolicyDetailResponseAsync(_organizationHasVerifiedDomainsQuery);
|
return await policy.GetSingleOrgPolicyDetailResponseAsync(_organizationHasVerifiedDomainsQuery);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,6 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obsolete.
|
/// Obsolete.
|
||||||
///
|
|
||||||
/// See <see cref="UserIsClaimedByOrganization"/>
|
/// See <see cref="UserIsClaimedByOrganization"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Please use UserIsClaimedByOrganization instead. This property will be removed in a future version.")]
|
[Obsolete("Please use UserIsClaimedByOrganization instead. This property will be removed in a future version.")]
|
||||||
@ -146,15 +145,12 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
set => UserIsClaimedByOrganization = value;
|
set => UserIsClaimedByOrganization = value;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates if the organization claims the user.
|
/// Indicates if the user is claimed by the organization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// An organization claims a user if the user's email domain is verified by the organization and the user is a member of it.
|
/// A user is claimed by an organization if the user's email domain is verified by the organization and the user is a member.
|
||||||
/// The organization must be enabled and able to have verified domains.
|
/// The organization must be enabled and able to have verified domains.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>
|
|
||||||
/// False if the Account Deprovisioning feature flag is disabled.
|
|
||||||
/// </returns>
|
|
||||||
public bool UserIsClaimedByOrganization { get; set; }
|
public bool UserIsClaimedByOrganization { get; set; }
|
||||||
public bool UseRiskInsights { get; set; }
|
public bool UseRiskInsights { get; set; }
|
||||||
public bool UseAdminSponsoredFamilies { get; set; }
|
public bool UseAdminSponsoredFamilies { get; set; }
|
||||||
|
@ -518,9 +518,8 @@ public class AccountsController : Controller
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If Account Deprovisioning is enabled, we need to check if the user is claimed by any organization.
|
// Check if the user is claimed by any organization.
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
if (await _userService.IsClaimedByAnyOrganizationAsync(user.Id))
|
||||||
&& await _userService.IsClaimedByAnyOrganizationAsync(user.Id))
|
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Cannot delete accounts owned by an organization. Contact your organization administrator for additional details.");
|
throw new BadRequestException("Cannot delete accounts owned by an organization. Contact your organization administrator for additional details.");
|
||||||
}
|
}
|
||||||
|
@ -1086,9 +1086,8 @@ public class CiphersController : Controller
|
|||||||
throw new BadRequestException(ModelState);
|
throw new BadRequestException(ModelState);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If Account Deprovisioning is enabled, we need to check if the user is claimed by any organization.
|
// Check if the user is claimed by any organization.
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
if (await _userService.IsClaimedByAnyOrganizationAsync(user.Id))
|
||||||
&& await _userService.IsClaimedByAnyOrganizationAsync(user.Id))
|
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Cannot purge accounts owned by an organization. Contact your organization administrator for additional details.");
|
throw new BadRequestException("Cannot purge accounts owned by an organization. Contact your organization administrator for additional details.");
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ public class VerifyOrganizationDomainCommand(
|
|||||||
IDnsResolverService dnsResolverService,
|
IDnsResolverService dnsResolverService,
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
IGlobalSettings globalSettings,
|
IGlobalSettings globalSettings,
|
||||||
IFeatureService featureService,
|
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
ISavePolicyCommand savePolicyCommand,
|
ISavePolicyCommand savePolicyCommand,
|
||||||
IMailService mailService,
|
IMailService mailService,
|
||||||
@ -125,11 +124,8 @@ public class VerifyOrganizationDomainCommand(
|
|||||||
|
|
||||||
private async Task DomainVerificationSideEffectsAsync(OrganizationDomain domain, IActingUser actingUser)
|
private async Task DomainVerificationSideEffectsAsync(OrganizationDomain domain, IActingUser actingUser)
|
||||||
{
|
{
|
||||||
if (featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
await EnableSingleOrganizationPolicyAsync(domain.OrganizationId, actingUser);
|
||||||
{
|
await SendVerifiedDomainUserEmailAsync(domain);
|
||||||
await EnableSingleOrganizationPolicyAsync(domain.OrganizationId, actingUser);
|
|
||||||
await SendVerifiedDomainUserEmailAsync(domain);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EnableSingleOrganizationPolicyAsync(Guid organizationId, IActingUser actingUser) =>
|
private async Task EnableSingleOrganizationPolicyAsync(Guid organizationId, IActingUser actingUser) =>
|
||||||
|
@ -159,7 +159,7 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
|
|||||||
throw new BadRequestException(RemoveAdminByCustomUserErrorMessage);
|
throw new BadRequestException(RemoveAdminByCustomUserErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) && deletingUserId.HasValue && eventSystemUser == null)
|
if (deletingUserId.HasValue && eventSystemUser == null)
|
||||||
{
|
{
|
||||||
var claimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(orgUser.OrganizationId, new[] { orgUser.Id });
|
var claimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(orgUser.OrganizationId, new[] { orgUser.Id });
|
||||||
if (claimedStatus.TryGetValue(orgUser.Id, out var isClaimed) && isClaimed)
|
if (claimedStatus.TryGetValue(orgUser.Id, out var isClaimed) && isClaimed)
|
||||||
@ -214,7 +214,7 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
|
|||||||
deletingUserIsOwner = await _currentContext.OrganizationOwner(organizationId);
|
deletingUserIsOwner = await _currentContext.OrganizationOwner(organizationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
var claimedStatus = _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) && deletingUserId.HasValue && eventSystemUser == null
|
var claimedStatus = deletingUserId.HasValue && eventSystemUser == null
|
||||||
? await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(organizationId, filteredUsers.Select(u => u.Id))
|
? await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(organizationId, filteredUsers.Select(u => u.Id))
|
||||||
: filteredUsers.ToDictionary(u => u.Id, u => false);
|
: filteredUsers.ToDictionary(u => u.Id, u => false);
|
||||||
var result = new List<(OrganizationUser OrganizationUser, string ErrorMessage)>();
|
var result = new List<(OrganizationUser OrganizationUser, string ErrorMessage)>();
|
||||||
|
@ -61,16 +61,9 @@ public class SingleOrgPolicyValidator : IPolicyValidator
|
|||||||
{
|
{
|
||||||
if (currentPolicy is not { Enabled: true } && policyUpdate is { Enabled: true })
|
if (currentPolicy is not { Enabled: true } && policyUpdate is { Enabled: true })
|
||||||
{
|
{
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
var currentUser = _currentContext.UserId ?? Guid.Empty;
|
||||||
{
|
var isOwnerOrProvider = await _currentContext.OrganizationOwner(policyUpdate.OrganizationId);
|
||||||
var currentUser = _currentContext.UserId ?? Guid.Empty;
|
await RevokeNonCompliantUsersAsync(policyUpdate.OrganizationId, policyUpdate.PerformedBy ?? new StandardUser(currentUser, isOwnerOrProvider));
|
||||||
var isOwnerOrProvider = await _currentContext.OrganizationOwner(policyUpdate.OrganizationId);
|
|
||||||
await RevokeNonCompliantUsersAsync(policyUpdate.OrganizationId, policyUpdate.PerformedBy ?? new StandardUser(currentUser, isOwnerOrProvider));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await RemoveNonCompliantUsersAsync(policyUpdate.OrganizationId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,42 +109,6 @@ public class SingleOrgPolicyValidator : IPolicyValidator
|
|||||||
_mailService.SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(organization.DisplayName(), x.Email)));
|
_mailService.SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(organization.DisplayName(), x.Email)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RemoveNonCompliantUsersAsync(Guid organizationId)
|
|
||||||
{
|
|
||||||
// Remove non-compliant users
|
|
||||||
var savingUserId = _currentContext.UserId;
|
|
||||||
// Note: must get OrganizationUserUserDetails so that Email is always populated from the User object
|
|
||||||
var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
|
|
||||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
|
||||||
if (org == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException(OrganizationNotFoundErrorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
var removableOrgUsers = orgUsers.Where(ou =>
|
|
||||||
ou.Status != OrganizationUserStatusType.Invited &&
|
|
||||||
ou.Status != OrganizationUserStatusType.Revoked &&
|
|
||||||
ou.Type != OrganizationUserType.Owner &&
|
|
||||||
ou.Type != OrganizationUserType.Admin &&
|
|
||||||
ou.UserId != savingUserId
|
|
||||||
).ToList();
|
|
||||||
|
|
||||||
var userOrgs = await _organizationUserRepository.GetManyByManyUsersAsync(
|
|
||||||
removableOrgUsers.Select(ou => ou.UserId!.Value));
|
|
||||||
foreach (var orgUser in removableOrgUsers)
|
|
||||||
{
|
|
||||||
if (userOrgs.Any(ou => ou.UserId == orgUser.UserId
|
|
||||||
&& ou.OrganizationId != org.Id
|
|
||||||
&& ou.Status != OrganizationUserStatusType.Invited))
|
|
||||||
{
|
|
||||||
await _removeOrganizationUserCommand.RemoveUserAsync(organizationId, orgUser.Id, savingUserId);
|
|
||||||
|
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(
|
|
||||||
org.DisplayName(), orgUser.Email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> ValidateAsync(PolicyUpdate policyUpdate, Policy? currentPolicy)
|
public async Task<string> ValidateAsync(PolicyUpdate policyUpdate, Policy? currentPolicy)
|
||||||
{
|
{
|
||||||
if (policyUpdate is not { Enabled: true })
|
if (policyUpdate is not { Enabled: true })
|
||||||
@ -165,8 +122,7 @@ public class SingleOrgPolicyValidator : IPolicyValidator
|
|||||||
return validateDecryptionErrorMessage;
|
return validateDecryptionErrorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
if (await _organizationHasVerifiedDomainsQuery.HasVerifiedDomainsAsync(policyUpdate.OrganizationId))
|
||||||
&& await _organizationHasVerifiedDomainsQuery.HasVerifiedDomainsAsync(policyUpdate.OrganizationId))
|
|
||||||
{
|
{
|
||||||
return ClaimedDomainSingleOrganizationRequiredErrorMessage;
|
return ClaimedDomainSingleOrganizationRequiredErrorMessage;
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,6 @@ public class TwoFactorAuthenticationPolicyValidator : IPolicyValidator
|
|||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||||
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
|
||||||
private readonly IFeatureService _featureService;
|
|
||||||
private readonly IRevokeNonCompliantOrganizationUserCommand _revokeNonCompliantOrganizationUserCommand;
|
private readonly IRevokeNonCompliantOrganizationUserCommand _revokeNonCompliantOrganizationUserCommand;
|
||||||
|
|
||||||
public const string NonCompliantMembersWillLoseAccessMessage = "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.";
|
public const string NonCompliantMembersWillLoseAccessMessage = "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.";
|
||||||
@ -38,8 +36,6 @@ public class TwoFactorAuthenticationPolicyValidator : IPolicyValidator
|
|||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||||
IRemoveOrganizationUserCommand removeOrganizationUserCommand,
|
|
||||||
IFeatureService featureService,
|
|
||||||
IRevokeNonCompliantOrganizationUserCommand revokeNonCompliantOrganizationUserCommand)
|
IRevokeNonCompliantOrganizationUserCommand revokeNonCompliantOrganizationUserCommand)
|
||||||
{
|
{
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
@ -47,8 +43,6 @@ public class TwoFactorAuthenticationPolicyValidator : IPolicyValidator
|
|||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||||
_removeOrganizationUserCommand = removeOrganizationUserCommand;
|
|
||||||
_featureService = featureService;
|
|
||||||
_revokeNonCompliantOrganizationUserCommand = revokeNonCompliantOrganizationUserCommand;
|
_revokeNonCompliantOrganizationUserCommand = revokeNonCompliantOrganizationUserCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,16 +50,9 @@ public class TwoFactorAuthenticationPolicyValidator : IPolicyValidator
|
|||||||
{
|
{
|
||||||
if (currentPolicy is not { Enabled: true } && policyUpdate is { Enabled: true })
|
if (currentPolicy is not { Enabled: true } && policyUpdate is { Enabled: true })
|
||||||
{
|
{
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
var currentUser = _currentContext.UserId ?? Guid.Empty;
|
||||||
{
|
var isOwnerOrProvider = await _currentContext.OrganizationOwner(policyUpdate.OrganizationId);
|
||||||
var currentUser = _currentContext.UserId ?? Guid.Empty;
|
await RevokeNonCompliantUsersAsync(policyUpdate.OrganizationId, policyUpdate.PerformedBy ?? new StandardUser(currentUser, isOwnerOrProvider));
|
||||||
var isOwnerOrProvider = await _currentContext.OrganizationOwner(policyUpdate.OrganizationId);
|
|
||||||
await RevokeNonCompliantUsersAsync(policyUpdate.OrganizationId, policyUpdate.PerformedBy ?? new StandardUser(currentUser, isOwnerOrProvider));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await RemoveNonCompliantUsersAsync(policyUpdate.OrganizationId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,40 +108,6 @@ public class TwoFactorAuthenticationPolicyValidator : IPolicyValidator
|
|||||||
_mailService.SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(organization.DisplayName(), x.Email)));
|
_mailService.SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(organization.DisplayName(), x.Email)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RemoveNonCompliantUsersAsync(Guid organizationId)
|
|
||||||
{
|
|
||||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
|
||||||
var savingUserId = _currentContext.UserId;
|
|
||||||
|
|
||||||
var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
|
|
||||||
var organizationUsersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUsers);
|
|
||||||
var removableOrgUsers = orgUsers.Where(ou =>
|
|
||||||
ou.Status != OrganizationUserStatusType.Invited && ou.Status != OrganizationUserStatusType.Revoked &&
|
|
||||||
ou.Type != OrganizationUserType.Owner && ou.Type != OrganizationUserType.Admin &&
|
|
||||||
ou.UserId != savingUserId);
|
|
||||||
|
|
||||||
// Reorder by HasMasterPassword to prioritize checking users without a master if they have 2FA enabled
|
|
||||||
foreach (var orgUser in removableOrgUsers.OrderBy(ou => ou.HasMasterPassword))
|
|
||||||
{
|
|
||||||
var userTwoFactorEnabled = organizationUsersTwoFactorEnabled.FirstOrDefault(u => u.user.Id == orgUser.Id)
|
|
||||||
.twoFactorIsEnabled;
|
|
||||||
if (!userTwoFactorEnabled)
|
|
||||||
{
|
|
||||||
if (!orgUser.HasMasterPassword)
|
|
||||||
{
|
|
||||||
throw new BadRequestException(
|
|
||||||
"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 _removeOrganizationUserCommand.RemoveUserAsync(organizationId, orgUser.Id,
|
|
||||||
savingUserId);
|
|
||||||
|
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
|
||||||
org!.DisplayName(), orgUser.Email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool MembersWithNoMasterPasswordWillLoseAccess(
|
private static bool MembersWithNoMasterPasswordWillLoseAccess(
|
||||||
IEnumerable<OrganizationUserUserDetails> orgUserDetails,
|
IEnumerable<OrganizationUserUserDetails> orgUserDetails,
|
||||||
IEnumerable<(OrganizationUserUserDetails user, bool isTwoFactorEnabled)> organizationUsersTwoFactorEnabled) =>
|
IEnumerable<(OrganizationUserUserDetails user, bool isTwoFactorEnabled)> organizationUsersTwoFactorEnabled) =>
|
||||||
|
@ -93,16 +93,8 @@ public class OrganizationDomainService : IOrganizationDomainService
|
|||||||
//Send email to administrators
|
//Send email to administrators
|
||||||
if (adminEmails.Count > 0)
|
if (adminEmails.Count > 0)
|
||||||
{
|
{
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
await _mailService.SendUnclaimedOrganizationDomainEmailAsync(adminEmails,
|
||||||
{
|
domain.OrganizationId.ToString(), domain.DomainName);
|
||||||
await _mailService.SendUnclaimedOrganizationDomainEmailAsync(adminEmails,
|
|
||||||
domain.OrganizationId.ToString(), domain.DomainName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _mailService.SendUnverifiedOrganizationDomainEmailAsync(adminEmails,
|
|
||||||
domain.OrganizationId.ToString(), domain.DomainName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Expired domain: {domainName}", domain.DomainName);
|
_logger.LogInformation(Constants.BypassFiltersEventId, "Expired domain: {domainName}", domain.DomainName);
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
{{#>FullHtmlLayout}}
|
|
||||||
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
<tr style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: left;" valign="top">
|
|
||||||
The domain {{DomainName}} in your Bitwarden organization could not be verified.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
|
||||||
Check the corresponding record in your domain host. Then reverify this domain in Bitwarden to use it for your organization.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
|
||||||
The domain will be removed from your organization in 7 days if it is not verified.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
|
||||||
<a href="{{{Url}}}" clicktracking=off target="_blank" style="color: #ffffff; text-decoration: none; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background-color: #175DDC; border-color: #175DDC; border-style: solid; border-width: 10px 20px; margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
Manage Domains
|
|
||||||
</a>
|
|
||||||
<br style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
{{/FullHtmlLayout}}
|
|
@ -1,10 +0,0 @@
|
|||||||
{{#>BasicTextLayout}}
|
|
||||||
The domain {{DomainName}} in your Bitwarden organization could not be verified.
|
|
||||||
|
|
||||||
Check the corresponding record in your domain host. Then reverify this domain in Bitwarden to use it for your organization.
|
|
||||||
|
|
||||||
The domain will be removed from your organization in 7 days if it is not verified.
|
|
||||||
|
|
||||||
{{Url}}
|
|
||||||
|
|
||||||
{{/BasicTextLayout}}
|
|
@ -1,9 +0,0 @@
|
|||||||
{{#>FullHtmlLayout}}
|
|
||||||
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top" align="left">
|
|
||||||
Your user account has been removed from the <b style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">{{OrganizationName}}</b> organization because you are a part of another organization. The {{OrganizationName}} organization has enabled a policy that prevents users from being a part of multiple organizations. Before you can re-join this organization you need to leave all other organizations or join with a different account.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
{{/FullHtmlLayout}}
|
|
@ -1,5 +0,0 @@
|
|||||||
{{#>BasicTextLayout}}
|
|
||||||
Your user account has been removed from the {{OrganizationName}} organization because you are a part of another
|
|
||||||
organization. The {{OrganizationName}} has enabled a policy that prevents users from being a part of multiple organizations. Before you can re-join this organization you need to leave all other organizations, or join with a
|
|
||||||
new account.
|
|
||||||
{{/BasicTextLayout}}
|
|
@ -1,15 +0,0 @@
|
|||||||
{{#>FullHtmlLayout}}
|
|
||||||
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top" align="left">
|
|
||||||
Your user account has been removed from the <b style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">{{OrganizationName}}</b> organization because you do not have two-step login configured. Before you can re-join this organization you need to set up two-step login on your user account.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
|
||||||
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;" valign="top" align="left">
|
|
||||||
Learn how to enable two-step login on your user account at
|
|
||||||
<a target="_blank" href="https://help.bitwarden.com/article/setup-two-step-login/" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #175DDC; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; text-decoration: underline;">https://help.bitwarden.com/article/setup-two-step-login/</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
{{/FullHtmlLayout}}
|
|
@ -1,7 +0,0 @@
|
|||||||
{{#>BasicTextLayout}}
|
|
||||||
Your user account has been removed from the {{OrganizationName}} organization because you do not have two-step login
|
|
||||||
configured. Before you can re-join this organization you need to set up two-step login on your user account.
|
|
||||||
|
|
||||||
Learn how to enable two-step login on your user account at
|
|
||||||
<https://help.bitwarden.com/article/setup-two-step-login/>
|
|
||||||
{{/BasicTextLayout}}
|
|
@ -40,7 +40,6 @@ public interface IMailService
|
|||||||
Task SendOrganizationAutoscaledEmailAsync(Organization organization, int initialSeatCount, IEnumerable<string> ownerEmails);
|
Task SendOrganizationAutoscaledEmailAsync(Organization organization, int initialSeatCount, IEnumerable<string> ownerEmails);
|
||||||
Task SendOrganizationAcceptedEmailAsync(Organization organization, string userIdentifier, IEnumerable<string> adminEmails, bool hasAccessSecretsManager = false);
|
Task SendOrganizationAcceptedEmailAsync(Organization organization, string userIdentifier, IEnumerable<string> adminEmails, bool hasAccessSecretsManager = false);
|
||||||
Task SendOrganizationConfirmedEmailAsync(string organizationName, string email, bool hasAccessSecretsManager = false);
|
Task SendOrganizationConfirmedEmailAsync(string organizationName, string email, bool hasAccessSecretsManager = false);
|
||||||
Task SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(string organizationName, string email);
|
|
||||||
Task SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(string organizationName, string email);
|
Task SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(string organizationName, string email);
|
||||||
Task SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(string organizationName, string email);
|
Task SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(string organizationName, string email);
|
||||||
Task SendPasswordlessSignInAsync(string returnUrl, string token, string email);
|
Task SendPasswordlessSignInAsync(string returnUrl, string token, string email);
|
||||||
@ -61,7 +60,6 @@ public interface IMailService
|
|||||||
Task SendLicenseExpiredAsync(IEnumerable<string> emails, string? organizationName = null);
|
Task SendLicenseExpiredAsync(IEnumerable<string> emails, string? organizationName = null);
|
||||||
Task SendNewDeviceLoggedInEmail(string email, string deviceType, DateTime timestamp, string ip);
|
Task SendNewDeviceLoggedInEmail(string email, string deviceType, DateTime timestamp, string ip);
|
||||||
Task SendRecoverTwoFactorEmail(string email, DateTime timestamp, string ip);
|
Task SendRecoverTwoFactorEmail(string email, DateTime timestamp, string ip);
|
||||||
Task SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(string organizationName, string email);
|
|
||||||
Task SendEmergencyAccessInviteEmailAsync(EmergencyAccess emergencyAccess, string name, string token);
|
Task SendEmergencyAccessInviteEmailAsync(EmergencyAccess emergencyAccess, string name, string token);
|
||||||
Task SendEmergencyAccessAcceptedEmailAsync(string granteeEmail, string email);
|
Task SendEmergencyAccessAcceptedEmailAsync(string granteeEmail, string email);
|
||||||
Task SendEmergencyAccessConfirmedEmailAsync(string grantorName, string email);
|
Task SendEmergencyAccessConfirmedEmailAsync(string grantorName, string email);
|
||||||
@ -88,7 +86,6 @@ public interface IMailService
|
|||||||
Task SendFamiliesForEnterpriseRedeemedEmailsAsync(string familyUserEmail, string sponsorEmail);
|
Task SendFamiliesForEnterpriseRedeemedEmailsAsync(string familyUserEmail, string sponsorEmail);
|
||||||
Task SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(string email, DateTime expirationDate);
|
Task SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(string email, DateTime expirationDate);
|
||||||
Task SendOTPEmailAsync(string email, string token);
|
Task SendOTPEmailAsync(string email, string token);
|
||||||
Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable<string> adminEmails, string organizationId, string domainName);
|
|
||||||
Task SendUnclaimedOrganizationDomainEmailAsync(IEnumerable<string> adminEmails, string organizationId, string domainName);
|
Task SendUnclaimedOrganizationDomainEmailAsync(IEnumerable<string> adminEmails, string organizationId, string domainName);
|
||||||
Task SendSecretsManagerMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable<string> ownerEmails);
|
Task SendSecretsManagerMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable<string> ownerEmails);
|
||||||
Task SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable<string> ownerEmails);
|
Task SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable<string> ownerEmails);
|
||||||
|
@ -133,16 +133,11 @@ public interface IUserService
|
|||||||
/// verified domains of that organization, and the user is a member of it.
|
/// verified domains of that organization, and the user is a member of it.
|
||||||
/// The organization must be enabled and able to have verified domains.
|
/// The organization must be enabled and able to have verified domains.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>
|
|
||||||
/// False if the Account Deprovisioning feature flag is disabled.
|
|
||||||
/// </returns>
|
|
||||||
Task<bool> IsClaimedByAnyOrganizationAsync(Guid userId);
|
Task<bool> IsClaimedByAnyOrganizationAsync(Guid userId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verify whether the new email domain meets the requirements for managed users.
|
/// Verify whether the new email domain meets the requirements for managed users.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// IdentityResult
|
/// IdentityResult
|
||||||
/// </returns>
|
/// </returns>
|
||||||
@ -151,9 +146,6 @@ public interface IUserService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the organizations that manage the user.
|
/// Gets the organizations that manage the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>
|
|
||||||
/// An empty collection if the Account Deprovisioning feature flag is disabled.
|
|
||||||
/// </returns>
|
|
||||||
/// <inheritdoc cref="IsClaimedByAnyOrganizationAsync"/>
|
/// <inheritdoc cref="IsClaimedByAnyOrganizationAsync"/>
|
||||||
Task<IEnumerable<Organization>> GetOrganizationsClaimingUserAsync(Guid userId);
|
Task<IEnumerable<Organization>> GetOrganizationsClaimingUserAsync(Guid userId);
|
||||||
}
|
}
|
||||||
|
@ -301,20 +301,6 @@ public class HandlebarsMailService : IMailService
|
|||||||
await EnqueueMailAsync(messageModels);
|
await EnqueueMailAsync(messageModels);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(string organizationName, string email)
|
|
||||||
{
|
|
||||||
var message = CreateDefaultMessage($"You have been removed from {organizationName}", email);
|
|
||||||
var model = new OrganizationUserRemovedForPolicyTwoStepViewModel
|
|
||||||
{
|
|
||||||
OrganizationName = CoreHelpers.SanitizeForEmail(organizationName, false),
|
|
||||||
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
|
||||||
SiteName = _globalSettings.SiteName
|
|
||||||
};
|
|
||||||
await AddMessageContentAsync(message, "OrganizationUserRemovedForPolicyTwoStep", model);
|
|
||||||
message.Category = "OrganizationUserRemovedForPolicyTwoStep";
|
|
||||||
await _mailDeliveryService.SendEmailAsync(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(string organizationName, string email)
|
public async Task SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(string organizationName, string email)
|
||||||
{
|
{
|
||||||
var message = CreateDefaultMessage($"You have been revoked from {organizationName}", email);
|
var message = CreateDefaultMessage($"You have been revoked from {organizationName}", email);
|
||||||
@ -532,20 +518,6 @@ public class HandlebarsMailService : IMailService
|
|||||||
await _mailDeliveryService.SendEmailAsync(message);
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(string organizationName, string email)
|
|
||||||
{
|
|
||||||
var message = CreateDefaultMessage($"You have been removed from {organizationName}", email);
|
|
||||||
var model = new OrganizationUserRemovedForPolicySingleOrgViewModel
|
|
||||||
{
|
|
||||||
OrganizationName = CoreHelpers.SanitizeForEmail(organizationName, false),
|
|
||||||
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
|
||||||
SiteName = _globalSettings.SiteName
|
|
||||||
};
|
|
||||||
await AddMessageContentAsync(message, "OrganizationUserRemovedForPolicySingleOrg", model);
|
|
||||||
message.Category = "OrganizationUserRemovedForPolicySingleOrg";
|
|
||||||
await _mailDeliveryService.SendEmailAsync(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(string organizationName, string email)
|
public async Task SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(string organizationName, string email)
|
||||||
{
|
{
|
||||||
var message = CreateDefaultMessage($"You have been revoked from {organizationName}", email);
|
var message = CreateDefaultMessage($"You have been revoked from {organizationName}", email);
|
||||||
@ -1137,19 +1109,6 @@ public class HandlebarsMailService : IMailService
|
|||||||
await _mailDeliveryService.SendEmailAsync(message);
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable<string> adminEmails, string organizationId, string domainName)
|
|
||||||
{
|
|
||||||
var message = CreateDefaultMessage("Domain not verified", adminEmails);
|
|
||||||
var model = new OrganizationDomainUnverifiedViewModel
|
|
||||||
{
|
|
||||||
Url = $"{_globalSettings.BaseServiceUri.VaultWithHash}/organizations/{organizationId}/settings/domain-verification",
|
|
||||||
DomainName = domainName
|
|
||||||
};
|
|
||||||
await AddMessageContentAsync(message, "OrganizationDomainUnverified", model);
|
|
||||||
message.Category = "UnverifiedOrganizationDomain";
|
|
||||||
await _mailDeliveryService.SendEmailAsync(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SendUnclaimedOrganizationDomainEmailAsync(IEnumerable<string> adminEmails, string organizationId, string domainName)
|
public async Task SendUnclaimedOrganizationDomainEmailAsync(IEnumerable<string> adminEmails, string organizationId, string domainName)
|
||||||
{
|
{
|
||||||
var message = CreateDefaultMessage("Domain not claimed", adminEmails);
|
var message = CreateDefaultMessage("Domain not claimed", adminEmails);
|
||||||
|
@ -1336,11 +1336,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
|
|
||||||
public async Task<IEnumerable<Organization>> GetOrganizationsClaimingUserAsync(Guid userId)
|
public async Task<IEnumerable<Organization>> GetOrganizationsClaimingUserAsync(Guid userId)
|
||||||
{
|
{
|
||||||
if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
|
||||||
{
|
|
||||||
return Enumerable.Empty<Organization>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all organizations that have verified the user's email domain.
|
// Get all organizations that have verified the user's email domain.
|
||||||
var organizationsWithVerifiedUserEmailDomain = await _organizationRepository.GetByVerifiedUserEmailDomainAsync(userId);
|
var organizationsWithVerifiedUserEmailDomain = await _organizationRepository.GetByVerifiedUserEmailDomainAsync(userId);
|
||||||
|
|
||||||
@ -1405,22 +1400,12 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
var removeOrgUserTasks = twoFactorPolicies.Select(async p =>
|
var removeOrgUserTasks = twoFactorPolicies.Select(async p =>
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(p.OrganizationId);
|
var organization = await _organizationRepository.GetByIdAsync(p.OrganizationId);
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
await _revokeNonCompliantOrganizationUserCommand.RevokeNonCompliantOrganizationUsersAsync(
|
||||||
{
|
new RevokeOrganizationUsersRequest(
|
||||||
await _revokeNonCompliantOrganizationUserCommand.RevokeNonCompliantOrganizationUsersAsync(
|
p.OrganizationId,
|
||||||
new RevokeOrganizationUsersRequest(
|
[new OrganizationUserUserDetails { Id = p.OrganizationUserId, OrganizationId = p.OrganizationId }],
|
||||||
p.OrganizationId,
|
new SystemUser(EventSystemUser.TwoFactorDisabled)));
|
||||||
[new OrganizationUserUserDetails { Id = p.OrganizationUserId, OrganizationId = p.OrganizationId }],
|
await _mailService.SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(organization.DisplayName(), user.Email);
|
||||||
new SystemUser(EventSystemUser.TwoFactorDisabled)));
|
|
||||||
await _mailService.SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(organization.DisplayName(), user.Email);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _removeOrganizationUserCommand.RemoveUserAsync(p.OrganizationId, user.Id);
|
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
|
||||||
organization.DisplayName(), user.Email);
|
|
||||||
}
|
|
||||||
|
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
await Task.WhenAll(removeOrgUserTasks);
|
await Task.WhenAll(removeOrgUserTasks);
|
||||||
|
@ -80,11 +80,6 @@ public class NoopMailService : IMailService
|
|||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(string organizationName, string email)
|
|
||||||
{
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(string organizationName, string email) =>
|
public Task SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(string organizationName, string email) =>
|
||||||
Task.CompletedTask;
|
Task.CompletedTask;
|
||||||
|
|
||||||
@ -155,11 +150,6 @@ public class NoopMailService : IMailService
|
|||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(string organizationName, string email)
|
|
||||||
{
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SendEmergencyAccessInviteEmailAsync(EmergencyAccess emergencyAccess, string name, string token)
|
public Task SendEmergencyAccessInviteEmailAsync(EmergencyAccess emergencyAccess, string name, string token)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
@ -268,11 +258,6 @@ public class NoopMailService : IMailService
|
|||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable<string> adminEmails, string organizationId, string domainName)
|
|
||||||
{
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SendUnclaimedOrganizationDomainEmailAsync(IEnumerable<string> adminEmails, string organizationId, string domainName)
|
public Task SendUnclaimedOrganizationDomainEmailAsync(IEnumerable<string> adminEmails, string organizationId, string domainName)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
|
@ -238,20 +238,13 @@ public class OrganizationUsersControllerTests
|
|||||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.Invite(organizationAbility.Id, model));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.Invite(organizationAbility.Id, model));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory, BitAutoData]
|
||||||
[BitAutoData(true)]
|
|
||||||
[BitAutoData(false)]
|
|
||||||
public async Task Get_ReturnsUser(
|
public async Task Get_ReturnsUser(
|
||||||
bool accountDeprovisioningEnabled,
|
|
||||||
OrganizationUserUserDetails organizationUser, ICollection<CollectionAccessSelection> collections,
|
OrganizationUserUserDetails organizationUser, ICollection<CollectionAccessSelection> collections,
|
||||||
SutProvider<OrganizationUsersController> sutProvider)
|
SutProvider<OrganizationUsersController> sutProvider)
|
||||||
{
|
{
|
||||||
organizationUser.Permissions = null;
|
organizationUser.Permissions = null;
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(accountDeprovisioningEnabled);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>()
|
sutProvider.GetDependency<ICurrentContext>()
|
||||||
.ManageUsers(organizationUser.OrganizationId)
|
.ManageUsers(organizationUser.OrganizationId)
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
@ -267,8 +260,8 @@ public class OrganizationUsersControllerTests
|
|||||||
var response = await sutProvider.Sut.Get(organizationUser.Id, false);
|
var response = await sutProvider.Sut.Get(organizationUser.Id, false);
|
||||||
|
|
||||||
Assert.Equal(organizationUser.Id, response.Id);
|
Assert.Equal(organizationUser.Id, response.Id);
|
||||||
Assert.Equal(accountDeprovisioningEnabled, response.ManagedByOrganization);
|
Assert.True(response.ManagedByOrganization);
|
||||||
Assert.Equal(accountDeprovisioningEnabled, response.ClaimedByOrganization);
|
Assert.True(response.ClaimedByOrganization);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
@ -140,7 +140,6 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
_currentContext.OrganizationUser(orgId).Returns(true);
|
_currentContext.OrganizationUser(orgId).Returns(true);
|
||||||
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
||||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||||
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
|
|
||||||
_userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List<Organization> { null });
|
_userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List<Organization> { null });
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Leave(orgId));
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Leave(orgId));
|
||||||
|
|
||||||
@ -170,7 +169,6 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
_currentContext.OrganizationUser(orgId).Returns(true);
|
_currentContext.OrganizationUser(orgId).Returns(true);
|
||||||
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
||||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||||
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
|
|
||||||
_userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List<Organization> { { foundOrg } });
|
_userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List<Organization> { { foundOrg } });
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Leave(orgId));
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Leave(orgId));
|
||||||
|
|
||||||
@ -205,7 +203,6 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
_currentContext.OrganizationUser(orgId).Returns(true);
|
_currentContext.OrganizationUser(orgId).Returns(true);
|
||||||
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
||||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||||
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
|
|
||||||
_userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List<Organization>());
|
_userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List<Organization>());
|
||||||
|
|
||||||
await _sut.Leave(orgId);
|
await _sut.Leave(orgId);
|
||||||
|
@ -7,7 +7,6 @@ using Bit.Api.Auth.Models.Request.WebAuthn;
|
|||||||
using Bit.Api.KeyManagement.Validators;
|
using Bit.Api.KeyManagement.Validators;
|
||||||
using Bit.Api.Tools.Models.Request;
|
using Bit.Api.Tools.Models.Request;
|
||||||
using Bit.Api.Vault.Models.Request;
|
using Bit.Api.Vault.Models.Request;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Auth.Entities;
|
using Bit.Core.Auth.Entities;
|
||||||
@ -193,21 +192,6 @@ public class AccountsControllerTests : IDisposable
|
|||||||
await _userService.Received(1).ChangeEmailAsync(user, default, default, default, default, default);
|
await _userService.Received(1).ChangeEmailAsync(user, default, default, default, default, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task PostEmail_WithAccountDeprovisioningEnabled_WhenUserIsNotManagedByAnOrganization_ShouldChangeUserEmail()
|
|
||||||
{
|
|
||||||
var user = GenerateExampleUser();
|
|
||||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
|
||||||
_userService.ChangeEmailAsync(user, default, default, default, default, default)
|
|
||||||
.Returns(Task.FromResult(IdentityResult.Success));
|
|
||||||
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
|
|
||||||
_userService.IsClaimedByAnyOrganizationAsync(user.Id).Returns(false);
|
|
||||||
|
|
||||||
await _sut.PostEmail(new EmailRequestModel());
|
|
||||||
|
|
||||||
await _userService.Received(1).ChangeEmailAsync(user, default, default, default, default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task PostEmail_WhenNotAuthorized_ShouldThrownUnauthorizedAccessException()
|
public async Task PostEmail_WhenNotAuthorized_ShouldThrownUnauthorizedAccessException()
|
||||||
{
|
{
|
||||||
@ -537,12 +521,11 @@ public class AccountsControllerTests : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Delete_WhenAccountDeprovisioningIsEnabled_WithUserManagedByAnOrganization_ThrowsBadRequestException()
|
public async Task Delete_WithUserManagedByAnOrganization_ThrowsBadRequestException()
|
||||||
{
|
{
|
||||||
var user = GenerateExampleUser();
|
var user = GenerateExampleUser();
|
||||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||||
ConfigureUserServiceToAcceptPasswordFor(user);
|
ConfigureUserServiceToAcceptPasswordFor(user);
|
||||||
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
|
|
||||||
_userService.IsClaimedByAnyOrganizationAsync(user.Id).Returns(true);
|
_userService.IsClaimedByAnyOrganizationAsync(user.Id).Returns(true);
|
||||||
|
|
||||||
var result = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Delete(new SecretVerificationRequestModel()));
|
var result = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Delete(new SecretVerificationRequestModel()));
|
||||||
@ -551,12 +534,11 @@ public class AccountsControllerTests : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Delete_WhenAccountDeprovisioningIsEnabled_WithUserNotManagedByAnOrganization_ShouldSucceed()
|
public async Task Delete_WithUserNotManagedByAnOrganization_ShouldSucceed()
|
||||||
{
|
{
|
||||||
var user = GenerateExampleUser();
|
var user = GenerateExampleUser();
|
||||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||||
ConfigureUserServiceToAcceptPasswordFor(user);
|
ConfigureUserServiceToAcceptPasswordFor(user);
|
||||||
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
|
|
||||||
_userService.IsClaimedByAnyOrganizationAsync(user.Id).Returns(false);
|
_userService.IsClaimedByAnyOrganizationAsync(user.Id).Returns(false);
|
||||||
_userService.DeleteAsync(user).Returns(IdentityResult.Success);
|
_userService.DeleteAsync(user).Returns(IdentityResult.Success);
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ public class VerifyOrganizationDomainCommandTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeEnabled(
|
public async Task UserVerifyOrganizationDomainAsync_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeEnabled(
|
||||||
OrganizationDomain domain, Guid userId, SutProvider<VerifyOrganizationDomainCommand> sutProvider)
|
OrganizationDomain domain, Guid userId, SutProvider<VerifyOrganizationDomainCommand> sutProvider)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
||||||
@ -177,10 +177,6 @@ public class VerifyOrganizationDomainCommandTests
|
|||||||
.ResolveAsync(domain.DomainName, domain.Txt)
|
.ResolveAsync(domain.DomainName, domain.Txt)
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>()
|
sutProvider.GetDependency<ICurrentContext>()
|
||||||
.UserId.Returns(userId);
|
.UserId.Returns(userId);
|
||||||
|
|
||||||
@ -196,33 +192,7 @@ public class VerifyOrganizationDomainCommandTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningDisabled_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeNotBeEnabled(
|
public async Task UserVerifyOrganizationDomainAsync_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldNotBeEnabled(
|
||||||
OrganizationDomain domain, SutProvider<VerifyOrganizationDomainCommand> sutProvider)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
|
||||||
.GetClaimedDomainsByDomainNameAsync(domain.DomainName)
|
|
||||||
.Returns([]);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IDnsResolverService>()
|
|
||||||
.ResolveAsync(domain.DomainName, domain.Txt)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>()
|
|
||||||
.UserId.Returns(Guid.NewGuid());
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(false);
|
|
||||||
|
|
||||||
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISavePolicyCommand>()
|
|
||||||
.DidNotReceive()
|
|
||||||
.SaveAsync(Arg.Any<PolicyUpdate>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldNotBeEnabled(
|
|
||||||
OrganizationDomain domain, SutProvider<VerifyOrganizationDomainCommand> sutProvider)
|
OrganizationDomain domain, SutProvider<VerifyOrganizationDomainCommand> sutProvider)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
||||||
@ -236,10 +206,6 @@ public class VerifyOrganizationDomainCommandTests
|
|||||||
sutProvider.GetDependency<ICurrentContext>()
|
sutProvider.GetDependency<ICurrentContext>()
|
||||||
.UserId.Returns(Guid.NewGuid());
|
.UserId.Returns(Guid.NewGuid());
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISavePolicyCommand>()
|
await sutProvider.GetDependency<ISavePolicyCommand>()
|
||||||
@ -248,33 +214,7 @@ public class VerifyOrganizationDomainCommandTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningDisabled_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldBeNotBeEnabled(
|
public async Task UserVerifyOrganizationDomainAsync_WhenDomainIsVerified_ThenEmailShouldBeSentToUsersWhoBelongToTheDomain(
|
||||||
OrganizationDomain domain, SutProvider<VerifyOrganizationDomainCommand> sutProvider)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
|
||||||
.GetClaimedDomainsByDomainNameAsync(domain.DomainName)
|
|
||||||
.Returns([]);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IDnsResolverService>()
|
|
||||||
.ResolveAsync(domain.DomainName, domain.Txt)
|
|
||||||
.Returns(false);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>()
|
|
||||||
.UserId.Returns(Guid.NewGuid());
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISavePolicyCommand>()
|
|
||||||
.DidNotReceive()
|
|
||||||
.SaveAsync(Arg.Any<PolicyUpdate>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsVerified_ThenEmailShouldBeSentToUsersWhoBelongToTheDomain(
|
|
||||||
ICollection<OrganizationUserUserDetails> organizationUsers,
|
ICollection<OrganizationUserUserDetails> organizationUsers,
|
||||||
OrganizationDomain domain,
|
OrganizationDomain domain,
|
||||||
Organization organization,
|
Organization organization,
|
||||||
@ -306,10 +246,6 @@ public class VerifyOrganizationDomainCommandTests
|
|||||||
sutProvider.GetDependency<ICurrentContext>()
|
sutProvider.GetDependency<ICurrentContext>()
|
||||||
.UserId.Returns(Guid.NewGuid());
|
.UserId.Returns(Guid.NewGuid());
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
.GetManyDetailsByOrganizationAsync(domain.OrganizationId)
|
.GetManyDetailsByOrganizationAsync(domain.OrganizationId)
|
||||||
.Returns(mockedUsers);
|
.Returns(mockedUsers);
|
||||||
|
@ -40,43 +40,6 @@ public class RemoveOrganizationUserCommandTests
|
|||||||
// Act
|
// Act
|
||||||
await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
|
await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
|
||||||
|
|
||||||
// Assert
|
|
||||||
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.GetUsersOrganizationClaimedStatusAsync(default, default);
|
|
||||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.Received(1)
|
|
||||||
.DeleteAsync(organizationUser);
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.Received(1)
|
|
||||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUser_WithDeletingUserId_WithAccountDeprovisioningEnabled_Success(
|
|
||||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser deletingUser,
|
|
||||||
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetByIdAsync(organizationUser.Id)
|
|
||||||
.Returns(organizationUser);
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetByIdAsync(deletingUser.Id)
|
|
||||||
.Returns(deletingUser);
|
|
||||||
sutProvider.GetDependency<ICurrentContext>()
|
|
||||||
.OrganizationOwner(deletingUser.OrganizationId)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@ -235,15 +198,12 @@ public class RemoveOrganizationUserCommandTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task RemoveUserAsync_WithDeletingUserId_WithAccountDeprovisioningEnabled_WhenUserIsManaged_ThrowsException(
|
public async Task RemoveUserAsync_WithDeletingUserId_WhenUserIsManaged_ThrowsException(
|
||||||
[OrganizationUser(status: OrganizationUserStatusType.Confirmed)] OrganizationUser orgUser,
|
[OrganizationUser(status: OrganizationUserStatusType.Confirmed)] OrganizationUser orgUser,
|
||||||
Guid deletingUserId,
|
Guid deletingUserId,
|
||||||
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
.GetByIdAsync(orgUser.Id)
|
.GetByIdAsync(orgUser.Id)
|
||||||
.Returns(orgUser);
|
.Returns(orgUser);
|
||||||
@ -285,34 +245,6 @@ public class RemoveOrganizationUserCommandTests
|
|||||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUser_WithEventSystemUser_WithAccountDeprovisioningEnabled_Success(
|
|
||||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser,
|
|
||||||
EventSystemUser eventSystemUser, SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetByIdAsync(organizationUser.Id)
|
|
||||||
.Returns(organizationUser);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.Id, eventSystemUser);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.GetUsersOrganizationClaimedStatusAsync(default, default);
|
|
||||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.Received(1)
|
|
||||||
.DeleteAsync(organizationUser);
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.Received(1)
|
|
||||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task RemoveUser_WithEventSystemUser_NotFound_ThrowsException(
|
public async Task RemoveUser_WithEventSystemUser_NotFound_ThrowsException(
|
||||||
@ -474,7 +406,6 @@ public class RemoveOrganizationUserCommandTests
|
|||||||
var sutProvider = SutProviderFactory();
|
var sutProvider = SutProviderFactory();
|
||||||
var eventDate = sutProvider.GetDependency<FakeTimeProvider>().GetUtcNow().UtcDateTime;
|
var eventDate = sutProvider.GetDependency<FakeTimeProvider>().GetUtcNow().UtcDateTime;
|
||||||
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
||||||
|
|
||||||
var organizationUsers = new[] { orgUser1, orgUser2 };
|
var organizationUsers = new[] { orgUser1, orgUser2 };
|
||||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
||||||
|
|
||||||
@ -499,60 +430,6 @@ public class RemoveOrganizationUserCommandTests
|
|||||||
// Act
|
// Act
|
||||||
var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(2, result.Count());
|
|
||||||
Assert.All(result, r => Assert.Empty(r.ErrorMessage));
|
|
||||||
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.GetUsersOrganizationClaimedStatusAsync(default, default);
|
|
||||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.Received(1)
|
|
||||||
.DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)));
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.Received(1)
|
|
||||||
.LogOrganizationUserEventsAsync(
|
|
||||||
Arg.Is<IEnumerable<(OrganizationUser OrganizationUser, EventType EventType, DateTime? DateTime)>>(i =>
|
|
||||||
i.First().OrganizationUser.Id == orgUser1.Id
|
|
||||||
&& i.Last().OrganizationUser.Id == orgUser2.Id
|
|
||||||
&& i.All(u => u.DateTime == eventDate)));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUsers_WithDeletingUserId_WithAccountDeprovisioningEnabled_Success(
|
|
||||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1, OrganizationUser orgUser2)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var sutProvider = SutProviderFactory();
|
|
||||||
var eventDate = sutProvider.GetDependency<FakeTimeProvider>().GetUtcNow().UtcDateTime;
|
|
||||||
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
|
||||||
var organizationUsers = new[] { orgUser1, orgUser2 };
|
|
||||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyAsync(default)
|
|
||||||
.ReturnsForAnyArgs(organizationUsers);
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetByIdAsync(deletingUser.Id)
|
|
||||||
.Returns(deletingUser);
|
|
||||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
|
||||||
.HasConfirmedOwnersExceptAsync(deletingUser.OrganizationId, Arg.Any<IEnumerable<Guid>>())
|
|
||||||
.Returns(true);
|
|
||||||
sutProvider.GetDependency<ICurrentContext>()
|
|
||||||
.OrganizationOwner(deletingUser.OrganizationId)
|
|
||||||
.Returns(true);
|
|
||||||
sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
|
||||||
.GetUsersOrganizationClaimedStatusAsync(
|
|
||||||
deletingUser.OrganizationId,
|
|
||||||
Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)))
|
|
||||||
.Returns(new Dictionary<Guid, bool> { { orgUser1.Id, false }, { orgUser2.Id, false } });
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(2, result.Count());
|
Assert.Equal(2, result.Count());
|
||||||
Assert.All(result, r => Assert.Empty(r.ErrorMessage));
|
Assert.All(result, r => Assert.Empty(r.ErrorMessage));
|
||||||
@ -638,7 +515,7 @@ public class RemoveOrganizationUserCommandTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task RemoveUsers_WithDeletingUserId_RemovingClaimedUser_WithAccountDeprovisioningEnabled_ThrowsException(
|
public async Task RemoveUsers_WithDeletingUserId_RemovingClaimedUser_ThrowsException(
|
||||||
[OrganizationUser(status: OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser,
|
[OrganizationUser(status: OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser,
|
||||||
OrganizationUser deletingUser,
|
OrganizationUser deletingUser,
|
||||||
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||||
@ -646,10 +523,6 @@ public class RemoveOrganizationUserCommandTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
orgUser.OrganizationId = deletingUser.OrganizationId;
|
orgUser.OrganizationId = deletingUser.OrganizationId;
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser.Id)))
|
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser.Id)))
|
||||||
.Returns(new[] { orgUser });
|
.Returns(new[] { orgUser });
|
||||||
@ -739,51 +612,6 @@ public class RemoveOrganizationUserCommandTests
|
|||||||
&& u.DateTime == eventDate)));
|
&& u.DateTime == eventDate)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task RemoveUsers_WithEventSystemUser_WithAccountDeprovisioningEnabled_Success(
|
|
||||||
EventSystemUser eventSystemUser,
|
|
||||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1,
|
|
||||||
OrganizationUser orgUser2)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var sutProvider = SutProviderFactory();
|
|
||||||
var eventDate = sutProvider.GetDependency<FakeTimeProvider>().GetUtcNow().UtcDateTime;
|
|
||||||
orgUser1.OrganizationId = orgUser2.OrganizationId;
|
|
||||||
var organizationUsers = new[] { orgUser1, orgUser2 };
|
|
||||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyAsync(default)
|
|
||||||
.ReturnsForAnyArgs(organizationUsers);
|
|
||||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
|
||||||
.HasConfirmedOwnersExceptAsync(orgUser1.OrganizationId, Arg.Any<IEnumerable<Guid>>())
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await sutProvider.Sut.RemoveUsersAsync(orgUser1.OrganizationId, organizationUserIds, eventSystemUser);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(2, result.Count());
|
|
||||||
Assert.All(result, r => Assert.Empty(r.ErrorMessage));
|
|
||||||
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.GetUsersOrganizationClaimedStatusAsync(default, default);
|
|
||||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.Received(1)
|
|
||||||
.DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)));
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.Received(1)
|
|
||||||
.LogOrganizationUserEventsAsync(
|
|
||||||
Arg.Is<IEnumerable<(OrganizationUser OrganizationUser, EventType EventType, EventSystemUser EventSystemUser, DateTime? DateTime)>>(
|
|
||||||
i => i.First().OrganizationUser.Id == orgUser1.Id
|
|
||||||
&& i.Last().OrganizationUser.Id == orgUser2.Id
|
|
||||||
&& i.All(u => u.EventSystemUser == eventSystemUser
|
|
||||||
&& u.DateTime == eventDate)));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task RemoveUsers_WithEventSystemUser_WithMismatchingOrganizationId_ThrowsException(
|
public async Task RemoveUsers_WithEventSystemUser_WithMismatchingOrganizationId_ThrowsException(
|
||||||
EventSystemUser eventSystemUser,
|
EventSystemUser eventSystemUser,
|
||||||
|
@ -122,9 +122,6 @@ public class SingleOrgPolicyValidatorTests
|
|||||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(savingUserId);
|
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(savingUserId);
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(policyUpdate.OrganizationId).Returns(organization);
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(policyUpdate.OrganizationId).Returns(organization);
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
||||||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>())
|
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>())
|
||||||
.Returns(new CommandResult());
|
.Returns(new CommandResult());
|
||||||
@ -148,161 +145,4 @@ public class SingleOrgPolicyValidatorTests
|
|||||||
.Received(1)
|
.Received(1)
|
||||||
.SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(organization.DisplayName(), nonCompliantUser.Email);
|
.SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(organization.DisplayName(), nonCompliantUser.Email);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task OnSaveSideEffectsAsync_RemovesNonCompliantUsers(
|
|
||||||
[PolicyUpdate(PolicyType.SingleOrg)] PolicyUpdate policyUpdate,
|
|
||||||
[Policy(PolicyType.SingleOrg, false)] Policy policy,
|
|
||||||
Guid savingUserId,
|
|
||||||
Guid nonCompliantUserId,
|
|
||||||
Organization organization, SutProvider<SingleOrgPolicyValidator> sutProvider)
|
|
||||||
{
|
|
||||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
|
||||||
|
|
||||||
var compliantUser1 = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
OrganizationId = organization.Id,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
UserId = new Guid(),
|
|
||||||
Email = "user1@example.com"
|
|
||||||
};
|
|
||||||
|
|
||||||
var compliantUser2 = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
OrganizationId = organization.Id,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
UserId = new Guid(),
|
|
||||||
Email = "user2@example.com"
|
|
||||||
};
|
|
||||||
|
|
||||||
var nonCompliantUser = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
OrganizationId = organization.Id,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
UserId = nonCompliantUserId,
|
|
||||||
Email = "user3@example.com"
|
|
||||||
};
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId)
|
|
||||||
.Returns([compliantUser1, compliantUser2, nonCompliantUser]);
|
|
||||||
|
|
||||||
var otherOrganizationUser = new OrganizationUser
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
OrganizationId = new Guid(),
|
|
||||||
UserId = nonCompliantUserId,
|
|
||||||
Status = OrganizationUserStatusType.Confirmed
|
|
||||||
};
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyByManyUsersAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(nonCompliantUserId)))
|
|
||||||
.Returns([otherOrganizationUser]);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(savingUserId);
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(policyUpdate.OrganizationId).Returns(organization);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(false);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
|
||||||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>())
|
|
||||||
.Returns(new CommandResult());
|
|
||||||
|
|
||||||
await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
|
|
||||||
.DidNotReceive()
|
|
||||||
.RemoveUserAsync(policyUpdate.OrganizationId, compliantUser1.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
|
|
||||||
.DidNotReceive()
|
|
||||||
.RemoveUserAsync(policyUpdate.OrganizationId, compliantUser2.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
|
|
||||||
.Received(1)
|
|
||||||
.RemoveUserAsync(policyUpdate.OrganizationId, nonCompliantUser.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IMailService>()
|
|
||||||
.DidNotReceive()
|
|
||||||
.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(organization.DisplayName(), compliantUser1.Email);
|
|
||||||
await sutProvider.GetDependency<IMailService>()
|
|
||||||
.DidNotReceive()
|
|
||||||
.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(organization.DisplayName(), compliantUser2.Email);
|
|
||||||
await sutProvider.GetDependency<IMailService>()
|
|
||||||
.Received(1)
|
|
||||||
.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(organization.DisplayName(), nonCompliantUser.Email);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task OnSaveSideEffectsAsync_WhenAccountDeprovisioningIsEnabled_ThenUsersAreRevoked(
|
|
||||||
[PolicyUpdate(PolicyType.SingleOrg)] PolicyUpdate policyUpdate,
|
|
||||||
[Policy(PolicyType.SingleOrg, false)] Policy policy,
|
|
||||||
Guid savingUserId,
|
|
||||||
Guid nonCompliantUserId,
|
|
||||||
Organization organization, SutProvider<SingleOrgPolicyValidator> sutProvider)
|
|
||||||
{
|
|
||||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
|
||||||
|
|
||||||
var compliantUser1 = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organization.Id,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
UserId = new Guid(),
|
|
||||||
Email = "user1@example.com"
|
|
||||||
};
|
|
||||||
|
|
||||||
var compliantUser2 = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organization.Id,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
UserId = new Guid(),
|
|
||||||
Email = "user2@example.com"
|
|
||||||
};
|
|
||||||
|
|
||||||
var nonCompliantUser = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organization.Id,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
UserId = nonCompliantUserId,
|
|
||||||
Email = "user3@example.com"
|
|
||||||
};
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId)
|
|
||||||
.Returns([compliantUser1, compliantUser2, nonCompliantUser]);
|
|
||||||
|
|
||||||
var otherOrganizationUser = new OrganizationUser
|
|
||||||
{
|
|
||||||
OrganizationId = new Guid(),
|
|
||||||
UserId = nonCompliantUserId,
|
|
||||||
Status = OrganizationUserStatusType.Confirmed
|
|
||||||
};
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyByManyUsersAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(nonCompliantUserId)))
|
|
||||||
.Returns([otherOrganizationUser]);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(savingUserId);
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(policyUpdate.OrganizationId)
|
|
||||||
.Returns(organization);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
|
||||||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>())
|
|
||||||
.Returns(new CommandResult());
|
|
||||||
|
|
||||||
await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
|
||||||
.Received()
|
|
||||||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
||||||
using Bit.Core.AdminConsole.Utilities.Commands;
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Context;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
@ -24,7 +23,7 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Policies.PolicyValidat
|
|||||||
public class TwoFactorAuthenticationPolicyValidatorTests
|
public class TwoFactorAuthenticationPolicyValidatorTests
|
||||||
{
|
{
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task OnSaveSideEffectsAsync_RemovesNonCompliantUsers(
|
public async Task OnSaveSideEffectsAsync_GivenNonCompliantUsersWithoutMasterPassword_Throws(
|
||||||
Organization organization,
|
Organization organization,
|
||||||
[PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate,
|
[PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate,
|
||||||
[Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy,
|
[Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy,
|
||||||
@ -33,249 +32,6 @@ public class TwoFactorAuthenticationPolicyValidatorTests
|
|||||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
|
||||||
var orgUserDetailUserInvited = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Invited,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user1@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = false
|
|
||||||
};
|
|
||||||
var orgUserDetailUserAcceptedWith2FA = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Accepted,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user2@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = true
|
|
||||||
};
|
|
||||||
var orgUserDetailUserAcceptedWithout2FA = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Accepted,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user3@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = true
|
|
||||||
};
|
|
||||||
var orgUserDetailAdmin = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
Type = OrganizationUserType.Admin,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "admin@test.com",
|
|
||||||
Name = "ADMIN",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = false
|
|
||||||
};
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId)
|
|
||||||
.Returns(new List<OrganizationUserUserDetails>
|
|
||||||
{
|
|
||||||
orgUserDetailUserInvited,
|
|
||||||
orgUserDetailUserAcceptedWith2FA,
|
|
||||||
orgUserDetailUserAcceptedWithout2FA,
|
|
||||||
orgUserDetailAdmin
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
|
||||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<OrganizationUserUserDetails>>())
|
|
||||||
.Returns(new List<(OrganizationUserUserDetails user, bool hasTwoFactor)>()
|
|
||||||
{
|
|
||||||
(orgUserDetailUserInvited, false),
|
|
||||||
(orgUserDetailUserAcceptedWith2FA, true),
|
|
||||||
(orgUserDetailUserAcceptedWithout2FA, false),
|
|
||||||
(orgUserDetailAdmin, false),
|
|
||||||
});
|
|
||||||
|
|
||||||
var savingUserId = Guid.NewGuid();
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(savingUserId);
|
|
||||||
|
|
||||||
await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy);
|
|
||||||
|
|
||||||
var removeOrganizationUserCommand = sutProvider.GetDependency<IRemoveOrganizationUserCommand>();
|
|
||||||
|
|
||||||
await removeOrganizationUserCommand.Received()
|
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWithout2FA.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IMailService>().Received()
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWithout2FA.Email);
|
|
||||||
|
|
||||||
await removeOrganizationUserCommand.DidNotReceive()
|
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserInvited.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserInvited.Email);
|
|
||||||
await removeOrganizationUserCommand.DidNotReceive()
|
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWith2FA.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWith2FA.Email);
|
|
||||||
await removeOrganizationUserCommand.DidNotReceive()
|
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailAdmin.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailAdmin.Email);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task OnSaveSideEffectsAsync_UsersToBeRemovedDontHaveMasterPasswords_Throws(
|
|
||||||
Organization organization,
|
|
||||||
[PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate,
|
|
||||||
[Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy,
|
|
||||||
SutProvider<TwoFactorAuthenticationPolicyValidator> sutProvider)
|
|
||||||
{
|
|
||||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
|
||||||
|
|
||||||
var orgUserDetailUserWith2FAAndMP = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user1@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = true
|
|
||||||
};
|
|
||||||
var orgUserDetailUserWith2FANoMP = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user2@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = false
|
|
||||||
};
|
|
||||||
var orgUserDetailUserWithout2FA = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user3@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = false
|
|
||||||
};
|
|
||||||
var orgUserDetailAdmin = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
Type = OrganizationUserType.Admin,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "admin@test.com",
|
|
||||||
Name = "ADMIN",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = false
|
|
||||||
};
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(false);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyDetailsByOrganizationAsync(policy.OrganizationId)
|
|
||||||
.Returns(new List<OrganizationUserUserDetails>
|
|
||||||
{
|
|
||||||
orgUserDetailUserWith2FAAndMP,
|
|
||||||
orgUserDetailUserWith2FANoMP,
|
|
||||||
orgUserDetailUserWithout2FA,
|
|
||||||
orgUserDetailAdmin
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
|
||||||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids =>
|
|
||||||
ids.Contains(orgUserDetailUserWith2FANoMP.UserId.Value)
|
|
||||||
&& ids.Contains(orgUserDetailUserWithout2FA.UserId.Value)
|
|
||||||
&& ids.Contains(orgUserDetailAdmin.UserId.Value)))
|
|
||||||
.Returns(new List<(Guid userId, bool hasTwoFactor)>()
|
|
||||||
{
|
|
||||||
(orgUserDetailUserWith2FANoMP.UserId.Value, true),
|
|
||||||
(orgUserDetailUserWithout2FA.UserId.Value, false),
|
|
||||||
(orgUserDetailAdmin.UserId.Value, false),
|
|
||||||
});
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy));
|
|
||||||
|
|
||||||
Assert.Equal(TwoFactorAuthenticationPolicyValidator.NonCompliantMembersWillLoseAccessMessage, badRequestException.Message);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>().DidNotReceiveWithAnyArgs()
|
|
||||||
.RemoveUserAsync(organizationId: default, organizationUserId: default, deletingUserId: default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task OnSaveSideEffectsAsync_GivenUpdateTo2faPolicy_WhenAccountProvisioningIsDisabled_ThenRevokeUserCommandShouldNotBeCalled(
|
|
||||||
Organization organization,
|
|
||||||
[PolicyUpdate(PolicyType.TwoFactorAuthentication)]
|
|
||||||
PolicyUpdate policyUpdate,
|
|
||||||
[Policy(PolicyType.TwoFactorAuthentication, false)]
|
|
||||||
Policy policy,
|
|
||||||
SutProvider<TwoFactorAuthenticationPolicyValidator> sutProvider)
|
|
||||||
{
|
|
||||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(false);
|
|
||||||
|
|
||||||
var orgUserDetailUserAcceptedWithout2Fa = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Accepted,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
Email = "user3@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = true
|
|
||||||
};
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId)
|
|
||||||
.Returns(new List<OrganizationUserUserDetails>
|
|
||||||
{
|
|
||||||
orgUserDetailUserAcceptedWithout2Fa
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
|
||||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<OrganizationUserUserDetails>>())
|
|
||||||
.Returns(new List<(OrganizationUserUserDetails user, bool hasTwoFactor)>()
|
|
||||||
{
|
|
||||||
(orgUserDetailUserAcceptedWithout2Fa, false),
|
|
||||||
});
|
|
||||||
|
|
||||||
await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
|
||||||
.DidNotReceive()
|
|
||||||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task OnSaveSideEffectsAsync_GivenUpdateTo2faPolicy_WhenAccountProvisioningIsEnabledAndUserDoesNotHaveMasterPassword_ThenNonCompliantMembersErrorMessageWillReturn(
|
|
||||||
Organization organization,
|
|
||||||
[PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate,
|
|
||||||
[Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy,
|
|
||||||
SutProvider<TwoFactorAuthenticationPolicyValidator> sutProvider)
|
|
||||||
{
|
|
||||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
var orgUserDetailUserWithout2Fa = new OrganizationUserUserDetails
|
var orgUserDetailUserWithout2Fa = new OrganizationUserUserDetails
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
@ -304,7 +60,7 @@ public class TwoFactorAuthenticationPolicyValidatorTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task OnSaveSideEffectsAsync_WhenAccountProvisioningIsEnabledAndUserHasMasterPassword_ThenUserWillBeRevoked(
|
public async Task OnSaveSideEffectsAsync_RevokesNonCompliantUsers(
|
||||||
Organization organization,
|
Organization organization,
|
||||||
[PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate,
|
[PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate,
|
||||||
[Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy,
|
[Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy,
|
||||||
@ -313,10 +69,6 @@ public class TwoFactorAuthenticationPolicyValidatorTests
|
|||||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
var orgUserDetailUserWithout2Fa = new OrganizationUserUserDetails
|
var orgUserDetailUserWithout2Fa = new OrganizationUserUserDetails
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
|
@ -343,28 +343,12 @@ public class UserServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningDisabled_ReturnsFalse(
|
public async Task IsClaimedByAnyOrganizationAsync_WithManagingEnabledOrganization_ReturnsTrue(
|
||||||
SutProvider<UserService> sutProvider, Guid userId)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(false);
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.IsClaimedByAnyOrganizationAsync(userId);
|
|
||||||
Assert.False(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithManagingEnabledOrganization_ReturnsTrue(
|
|
||||||
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
||||||
{
|
{
|
||||||
organization.Enabled = true;
|
organization.Enabled = true;
|
||||||
organization.UseSso = true;
|
organization.UseSso = true;
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
.GetByVerifiedUserEmailDomainAsync(userId)
|
.GetByVerifiedUserEmailDomainAsync(userId)
|
||||||
.Returns(new[] { organization });
|
.Returns(new[] { organization });
|
||||||
@ -374,16 +358,12 @@ public class UserServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithManagingDisabledOrganization_ReturnsFalse(
|
public async Task IsClaimedByAnyOrganizationAsync_WithManagingDisabledOrganization_ReturnsFalse(
|
||||||
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
||||||
{
|
{
|
||||||
organization.Enabled = false;
|
organization.Enabled = false;
|
||||||
organization.UseSso = true;
|
organization.UseSso = true;
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
.GetByVerifiedUserEmailDomainAsync(userId)
|
.GetByVerifiedUserEmailDomainAsync(userId)
|
||||||
.Returns(new[] { organization });
|
.Returns(new[] { organization });
|
||||||
@ -393,16 +373,12 @@ public class UserServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithOrganizationUseSsoFalse_ReturnsFalse(
|
public async Task IsClaimedByAnyOrganizationAsync_WithOrganizationUseSsoFalse_ReturnsFalse(
|
||||||
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
||||||
{
|
{
|
||||||
organization.Enabled = true;
|
organization.Enabled = true;
|
||||||
organization.UseSso = false;
|
organization.UseSso = false;
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
.GetByVerifiedUserEmailDomainAsync(userId)
|
.GetByVerifiedUserEmailDomainAsync(userId)
|
||||||
.Returns(new[] { organization });
|
.Returns(new[] { organization });
|
||||||
@ -412,100 +388,7 @@ public class UserServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task DisableTwoFactorProviderAsync_WhenOrganizationHas2FAPolicyEnabled_DisablingAllProviders_RemovesUserFromOrganizationAndSendsEmail(
|
public async Task DisableTwoFactorProviderAsync_WhenOrganizationHas2FAPolicyEnabled_DisablingAllProviders_RevokesUserAndSendsEmail(
|
||||||
SutProvider<UserService> sutProvider, User user, Organization organization)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
|
||||||
{
|
|
||||||
[TwoFactorProviderType.Email] = new() { Enabled = true }
|
|
||||||
});
|
|
||||||
sutProvider.GetDependency<IPolicyService>()
|
|
||||||
.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication)
|
|
||||||
.Returns(
|
|
||||||
[
|
|
||||||
new OrganizationUserPolicyDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organization.Id,
|
|
||||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
|
||||||
PolicyEnabled = true
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
|
||||||
.GetByIdAsync(organization.Id)
|
|
||||||
.Returns(organization);
|
|
||||||
var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary<TwoFactorProviderType, TwoFactorProvider>(), JsonHelpers.LegacyEnumKeyResolver);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await sutProvider.Sut.DisableTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await sutProvider.GetDependency<IUserRepository>()
|
|
||||||
.Received(1)
|
|
||||||
.ReplaceAsync(Arg.Is<User>(u => u.Id == user.Id && u.TwoFactorProviders == expectedSavedProviders));
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.Received(1)
|
|
||||||
.LogUserEventAsync(user.Id, EventType.User_Disabled2fa);
|
|
||||||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
|
|
||||||
.Received(1)
|
|
||||||
.RemoveUserAsync(organization.Id, user.Id);
|
|
||||||
await sutProvider.GetDependency<IMailService>()
|
|
||||||
.Received(1)
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), user.Email);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task DisableTwoFactorProviderAsync_WhenOrganizationHas2FAPolicyEnabled_UserHasOneProviderEnabled_DoesNotRemoveUserFromOrganization(
|
|
||||||
SutProvider<UserService> sutProvider, User user, Organization organization)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
|
||||||
{
|
|
||||||
[TwoFactorProviderType.Email] = new() { Enabled = true },
|
|
||||||
[TwoFactorProviderType.Remember] = new() { Enabled = true }
|
|
||||||
});
|
|
||||||
sutProvider.GetDependency<IPolicyService>()
|
|
||||||
.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication)
|
|
||||||
.Returns(
|
|
||||||
[
|
|
||||||
new OrganizationUserPolicyDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organization.Id,
|
|
||||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
|
||||||
PolicyEnabled = true
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
|
||||||
.GetByIdAsync(organization.Id)
|
|
||||||
.Returns(organization);
|
|
||||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
|
||||||
.TwoFactorIsEnabledAsync(user)
|
|
||||||
.Returns(true);
|
|
||||||
var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
|
||||||
{
|
|
||||||
[TwoFactorProviderType.Remember] = new() { Enabled = true }
|
|
||||||
}, JsonHelpers.LegacyEnumKeyResolver);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await sutProvider.Sut.DisableTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await sutProvider.GetDependency<IUserRepository>()
|
|
||||||
.Received(1)
|
|
||||||
.ReplaceAsync(Arg.Is<User>(u => u.Id == user.Id && u.TwoFactorProviders == expectedSavedProviders));
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.Received(1)
|
|
||||||
.LogUserEventAsync(user.Id, EventType.User_Disabled2fa);
|
|
||||||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.RemoveUserAsync(default, default);
|
|
||||||
await sutProvider.GetDependency<IMailService>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task DisableTwoFactorProviderAsync_WithAccountDeprovisioningEnabled_WhenOrganizationHas2FAPolicyEnabled_DisablingAllProviders_RevokesUserAndSendsEmail(
|
|
||||||
SutProvider<UserService> sutProvider, User user,
|
SutProvider<UserService> sutProvider, User user,
|
||||||
Organization organization1, Guid organizationUserId1,
|
Organization organization1, Guid organizationUserId1,
|
||||||
Organization organization2, Guid organizationUserId2)
|
Organization organization2, Guid organizationUserId2)
|
||||||
@ -518,9 +401,6 @@ public class UserServiceTests
|
|||||||
organization1.Enabled = organization2.Enabled = true;
|
organization1.Enabled = organization2.Enabled = true;
|
||||||
organization1.UseSso = organization2.UseSso = true;
|
organization1.UseSso = organization2.UseSso = true;
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
sutProvider.GetDependency<IPolicyService>()
|
sutProvider.GetDependency<IPolicyService>()
|
||||||
.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication)
|
.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication)
|
||||||
.Returns(
|
.Returns(
|
||||||
@ -583,7 +463,7 @@ public class UserServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task DisableTwoFactorProviderAsync_WithAccountDeprovisioningEnabled_UserHasOneProviderEnabled_DoesNotRemoveUserFromOrganization(
|
public async Task DisableTwoFactorProviderAsync_UserHasOneProviderEnabled_DoesNotRevokeUserFromOrganization(
|
||||||
SutProvider<UserService> sutProvider, User user, Organization organization)
|
SutProvider<UserService> sutProvider, User user, Organization organization)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
@ -606,6 +486,9 @@ public class UserServiceTests
|
|||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
.GetByIdAsync(organization.Id)
|
.GetByIdAsync(organization.Id)
|
||||||
.Returns(organization);
|
.Returns(organization);
|
||||||
|
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||||
|
.TwoFactorIsEnabledAsync(user)
|
||||||
|
.Returns(true);
|
||||||
var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||||
{
|
{
|
||||||
[TwoFactorProviderType.Remember] = new() { Enabled = true }
|
[TwoFactorProviderType.Remember] = new() { Enabled = true }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user