From a1b22e66e5a8d0cabd49e59c25b0993f391b2dd8 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Tue, 13 May 2025 07:17:54 +1000 Subject: [PATCH] [PM-14613] Remove account deprovisioning feature flag (#5676) * Remove flag * Remove old tests * Remove old xmldoc referencing the flag * Remove old emails --- src/Admin/Controllers/UsersController.cs | 13 +- .../OrganizationUsersController.cs | 7 - .../Controllers/OrganizationsController.cs | 3 +- .../Controllers/PoliciesController.cs | 3 +- .../ProfileOrganizationResponseModel.cs | 8 +- .../Auth/Controllers/AccountsController.cs | 5 +- .../Vault/Controllers/CiphersController.cs | 5 +- .../VerifyOrganizationDomainCommand.cs | 8 +- .../RemoveOrganizationUserCommand.cs | 4 +- .../SingleOrgPolicyValidator.cs | 52 +--- .../TwoFactorAuthenticationPolicyValidator.cs | 53 +--- .../OrganizationDomainService.cs | 12 +- .../OrganizationDomainUnverified.html.hbs | 27 -- .../OrganizationDomainUnverified.text.hbs | 10 - ...tionUserRemovedForPolicySingleOrg.html.hbs | 9 - ...tionUserRemovedForPolicySingleOrg.text.hbs | 5 - ...zationUserRemovedForPolicyTwoStep.html.hbs | 15 -- ...zationUserRemovedForPolicyTwoStep.text.hbs | 7 - src/Core/Services/IMailService.cs | 3 - src/Core/Services/IUserService.cs | 8 - .../Implementations/HandlebarsMailService.cs | 41 --- .../Services/Implementations/UserService.cs | 27 +- .../NoopImplementations/NoopMailService.cs | 15 -- .../OrganizationUsersControllerTests.cs | 13 +- .../OrganizationsControllerTests.cs | 3 - .../Controllers/AccountsControllerTests.cs | 22 +- .../VerifyOrganizationDomainCommandTests.cs | 70 +---- .../RemoveOrganizationUserCommandTests.cs | 176 +----------- .../SingleOrgPolicyValidatorTests.cs | 160 ----------- ...actorAuthenticationPolicyValidatorTests.cs | 252 +----------------- test/Core.Test/Services/UserServiceTests.cs | 133 +-------- 31 files changed, 49 insertions(+), 1120 deletions(-) delete mode 100644 src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.html.hbs delete mode 100644 src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.text.hbs delete mode 100644 src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicySingleOrg.html.hbs delete mode 100644 src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicySingleOrg.text.hbs delete mode 100644 src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.html.hbs delete mode 100644 src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.text.hbs diff --git a/src/Admin/Controllers/UsersController.cs b/src/Admin/Controllers/UsersController.cs index cecd7a2142..b85a91719c 100644 --- a/src/Admin/Controllers/UsersController.cs +++ b/src/Admin/Controllers/UsersController.cs @@ -4,7 +4,6 @@ using Bit.Admin.Enums; using Bit.Admin.Models; using Bit.Admin.Services; using Bit.Admin.Utilities; -using Bit.Core; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; @@ -89,7 +88,7 @@ public class UsersController : Controller var ciphers = await _cipherRepository.GetManyByUserIdAsync(id); 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)); } @@ -106,7 +105,7 @@ public class UsersController : Controller var billingInfo = await _paymentService.GetBillingAsync(user); var billingHistoryInfo = await _paymentService.GetBillingHistoryAsync(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); 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); return RedirectToAction("Edit", new { id }); } - - // TODO: Feature flag to be removed in PM-14207 - private async Task AccountDeprovisioningEnabled(Guid userId) - { - return _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - ? await _userService.IsClaimedByAnyOrganizationAsync(userId) - : null; - } } diff --git a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index 536914b56f..6b23edf347 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -616,7 +616,6 @@ public class OrganizationUsersController : Controller new OrganizationUserBulkResponseModel(r.OrganizationUserId, r.ErrorMessage))); } - [RequireFeature(FeatureFlagKeys.AccountDeprovisioning)] [HttpDelete("{id}/delete-account")] [HttpPost("{id}/delete-account")] public async Task DeleteAccount(Guid orgId, Guid id) @@ -635,7 +634,6 @@ public class OrganizationUsersController : Controller await _deleteClaimedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUser.Id); } - [RequireFeature(FeatureFlagKeys.AccountDeprovisioning)] [HttpDelete("delete-account")] [HttpPost("delete-account")] public async Task> BulkDeleteAccount(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model) @@ -760,11 +758,6 @@ public class OrganizationUsersController : Controller private async Task> GetClaimedByOrganizationStatusAsync(Guid orgId, IEnumerable userIds) { - if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)) - { - return userIds.ToDictionary(kvp => kvp, kvp => false); - } - var usersOrganizationClaimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(orgId, userIds); return usersOrganizationClaimedStatus; } diff --git a/src/Api/AdminConsole/Controllers/OrganizationsController.cs b/src/Api/AdminConsole/Controllers/OrganizationsController.cs index c856c8ab91..f402c927e0 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationsController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationsController.cs @@ -279,8 +279,7 @@ public class OrganizationsController : Controller throw new BadRequestException("Your organization's Single Sign-On settings prevent you from leaving."); } - if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - && (await _userService.GetOrganizationsClaimingUserAsync(user.Id)).Any(x => x.Id == id)) + if ((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."); } diff --git a/src/Api/AdminConsole/Controllers/PoliciesController.cs b/src/Api/AdminConsole/Controllers/PoliciesController.cs index 7de6f6e730..86a1609ee6 100644 --- a/src/Api/AdminConsole/Controllers/PoliciesController.cs +++ b/src/Api/AdminConsole/Controllers/PoliciesController.cs @@ -2,7 +2,6 @@ using Bit.Api.AdminConsole.Models.Response.Helpers; using Bit.Api.AdminConsole.Models.Response.Organizations; using Bit.Api.Models.Response; -using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; @@ -79,7 +78,7 @@ public class PoliciesController : Controller 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); } diff --git a/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs index 5c900b73e0..259ce3e795 100644 --- a/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs @@ -136,7 +136,6 @@ public class ProfileOrganizationResponseModel : ResponseModel public bool AllowAdminAccessToAllCollectionItems { get; set; } /// /// Obsolete. - /// /// See /// [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; } /// - /// Indicates if the organization claims the user. + /// Indicates if the user is claimed by the organization. /// /// - /// 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. /// - /// - /// False if the Account Deprovisioning feature flag is disabled. - /// public bool UserIsClaimedByOrganization { get; set; } public bool UseRiskInsights { get; set; } public bool UseAdminSponsoredFamilies { get; set; } diff --git a/src/Api/Auth/Controllers/AccountsController.cs b/src/Api/Auth/Controllers/AccountsController.cs index fdd5fbb290..2499b269f5 100644 --- a/src/Api/Auth/Controllers/AccountsController.cs +++ b/src/Api/Auth/Controllers/AccountsController.cs @@ -518,9 +518,8 @@ public class AccountsController : Controller } else { - // If Account Deprovisioning is enabled, we need to check if the user is claimed by any organization. - if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - && await _userService.IsClaimedByAnyOrganizationAsync(user.Id)) + // Check if the user is claimed by any organization. + if (await _userService.IsClaimedByAnyOrganizationAsync(user.Id)) { throw new BadRequestException("Cannot delete accounts owned by an organization. Contact your organization administrator for additional details."); } diff --git a/src/Api/Vault/Controllers/CiphersController.cs b/src/Api/Vault/Controllers/CiphersController.cs index 03b83e3de2..02dace894d 100644 --- a/src/Api/Vault/Controllers/CiphersController.cs +++ b/src/Api/Vault/Controllers/CiphersController.cs @@ -1086,9 +1086,8 @@ public class CiphersController : Controller throw new BadRequestException(ModelState); } - // If Account Deprovisioning is enabled, we need to check if the user is claimed by any organization. - if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - && await _userService.IsClaimedByAnyOrganizationAsync(user.Id)) + // Check if the user is claimed by any organization. + if (await _userService.IsClaimedByAnyOrganizationAsync(user.Id)) { throw new BadRequestException("Cannot purge accounts owned by an organization. Contact your organization administrator for additional details."); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs index ec635282f7..43a3120ffd 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs @@ -20,7 +20,6 @@ public class VerifyOrganizationDomainCommand( IDnsResolverService dnsResolverService, IEventService eventService, IGlobalSettings globalSettings, - IFeatureService featureService, ICurrentContext currentContext, ISavePolicyCommand savePolicyCommand, IMailService mailService, @@ -125,11 +124,8 @@ public class VerifyOrganizationDomainCommand( 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) => diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommand.cs index 4de2cd0ea5..00d3ebb533 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommand.cs @@ -159,7 +159,7 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand 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 }); if (claimedStatus.TryGetValue(orgUser.Id, out var isClaimed) && isClaimed) @@ -214,7 +214,7 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand 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)) : filteredUsers.ToDictionary(u => u.Id, u => false); var result = new List<(OrganizationUser OrganizationUser, string ErrorMessage)>(); diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidator.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidator.cs index a37deef3eb..49467eaae4 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidator.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidator.cs @@ -61,16 +61,9 @@ public class SingleOrgPolicyValidator : IPolicyValidator { 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); - await RevokeNonCompliantUsersAsync(policyUpdate.OrganizationId, policyUpdate.PerformedBy ?? new StandardUser(currentUser, isOwnerOrProvider)); - } - else - { - await RemoveNonCompliantUsersAsync(policyUpdate.OrganizationId); - } + var currentUser = _currentContext.UserId ?? Guid.Empty; + var isOwnerOrProvider = await _currentContext.OrganizationOwner(policyUpdate.OrganizationId); + await RevokeNonCompliantUsersAsync(policyUpdate.OrganizationId, policyUpdate.PerformedBy ?? new StandardUser(currentUser, isOwnerOrProvider)); } } @@ -116,42 +109,6 @@ public class SingleOrgPolicyValidator : IPolicyValidator _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 ValidateAsync(PolicyUpdate policyUpdate, Policy? currentPolicy) { if (policyUpdate is not { Enabled: true }) @@ -165,8 +122,7 @@ public class SingleOrgPolicyValidator : IPolicyValidator return validateDecryptionErrorMessage; } - if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - && await _organizationHasVerifiedDomainsQuery.HasVerifiedDomainsAsync(policyUpdate.OrganizationId)) + if (await _organizationHasVerifiedDomainsQuery.HasVerifiedDomainsAsync(policyUpdate.OrganizationId)) { return ClaimedDomainSingleOrganizationRequiredErrorMessage; } diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidator.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidator.cs index c757a65913..13cc935eb9 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidator.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidator.cs @@ -23,8 +23,6 @@ public class TwoFactorAuthenticationPolicyValidator : IPolicyValidator private readonly IOrganizationRepository _organizationRepository; private readonly ICurrentContext _currentContext; private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery; - private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand; - private readonly IFeatureService _featureService; 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."; @@ -38,8 +36,6 @@ public class TwoFactorAuthenticationPolicyValidator : IPolicyValidator IOrganizationRepository organizationRepository, ICurrentContext currentContext, ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery, - IRemoveOrganizationUserCommand removeOrganizationUserCommand, - IFeatureService featureService, IRevokeNonCompliantOrganizationUserCommand revokeNonCompliantOrganizationUserCommand) { _organizationUserRepository = organizationUserRepository; @@ -47,8 +43,6 @@ public class TwoFactorAuthenticationPolicyValidator : IPolicyValidator _organizationRepository = organizationRepository; _currentContext = currentContext; _twoFactorIsEnabledQuery = twoFactorIsEnabledQuery; - _removeOrganizationUserCommand = removeOrganizationUserCommand; - _featureService = featureService; _revokeNonCompliantOrganizationUserCommand = revokeNonCompliantOrganizationUserCommand; } @@ -56,16 +50,9 @@ public class TwoFactorAuthenticationPolicyValidator : IPolicyValidator { 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); - await RevokeNonCompliantUsersAsync(policyUpdate.OrganizationId, policyUpdate.PerformedBy ?? new StandardUser(currentUser, isOwnerOrProvider)); - } - else - { - await RemoveNonCompliantUsersAsync(policyUpdate.OrganizationId); - } + var currentUser = _currentContext.UserId ?? Guid.Empty; + var isOwnerOrProvider = await _currentContext.OrganizationOwner(policyUpdate.OrganizationId); + await RevokeNonCompliantUsersAsync(policyUpdate.OrganizationId, policyUpdate.PerformedBy ?? new StandardUser(currentUser, isOwnerOrProvider)); } } @@ -121,40 +108,6 @@ public class TwoFactorAuthenticationPolicyValidator : IPolicyValidator _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( IEnumerable orgUserDetails, IEnumerable<(OrganizationUserUserDetails user, bool isTwoFactorEnabled)> organizationUsersTwoFactorEnabled) => diff --git a/src/Core/AdminConsole/Services/Implementations/OrganizationDomainService.cs b/src/Core/AdminConsole/Services/Implementations/OrganizationDomainService.cs index 9b99cf71f0..854e486b42 100644 --- a/src/Core/AdminConsole/Services/Implementations/OrganizationDomainService.cs +++ b/src/Core/AdminConsole/Services/Implementations/OrganizationDomainService.cs @@ -93,16 +93,8 @@ public class OrganizationDomainService : IOrganizationDomainService //Send email to administrators if (adminEmails.Count > 0) { - if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)) - { - await _mailService.SendUnclaimedOrganizationDomainEmailAsync(adminEmails, - domain.OrganizationId.ToString(), domain.DomainName); - } - else - { - await _mailService.SendUnverifiedOrganizationDomainEmailAsync(adminEmails, - domain.OrganizationId.ToString(), domain.DomainName); - } + await _mailService.SendUnclaimedOrganizationDomainEmailAsync(adminEmails, + domain.OrganizationId.ToString(), domain.DomainName); } _logger.LogInformation(Constants.BypassFiltersEventId, "Expired domain: {domainName}", domain.DomainName); diff --git a/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.html.hbs deleted file mode 100644 index 11b482acda..0000000000 --- a/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.html.hbs +++ /dev/null @@ -1,27 +0,0 @@ -{{#>FullHtmlLayout}} - - - - - - - - - - - - - -
- 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. -
- - Manage Domains - -
-
-{{/FullHtmlLayout}} \ No newline at end of file diff --git a/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.text.hbs b/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.text.hbs deleted file mode 100644 index f056bf26c3..0000000000 --- a/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.text.hbs +++ /dev/null @@ -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}} \ No newline at end of file diff --git a/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicySingleOrg.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicySingleOrg.html.hbs deleted file mode 100644 index bd2e4eb946..0000000000 --- a/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicySingleOrg.html.hbs +++ /dev/null @@ -1,9 +0,0 @@ -{{#>FullHtmlLayout}} - - - - -
- Your user account has been removed from the {{OrganizationName}} 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. -
-{{/FullHtmlLayout}} diff --git a/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicySingleOrg.text.hbs b/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicySingleOrg.text.hbs deleted file mode 100644 index 44ef628a90..0000000000 --- a/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicySingleOrg.text.hbs +++ /dev/null @@ -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}} diff --git a/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.html.hbs deleted file mode 100644 index e82dfcef27..0000000000 --- a/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.html.hbs +++ /dev/null @@ -1,15 +0,0 @@ -{{#>FullHtmlLayout}} - - - - - - - -
- 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/ -
-{{/FullHtmlLayout}} diff --git a/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.text.hbs b/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.text.hbs deleted file mode 100644 index a79afb588a..0000000000 --- a/src/Core/MailTemplates/Handlebars/OrganizationUserRemovedForPolicyTwoStep.text.hbs +++ /dev/null @@ -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 - -{{/BasicTextLayout}} \ No newline at end of file diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs index 7de75a5143..aa1c0c8c25 100644 --- a/src/Core/Services/IMailService.cs +++ b/src/Core/Services/IMailService.cs @@ -40,7 +40,6 @@ public interface IMailService Task SendOrganizationAutoscaledEmailAsync(Organization organization, int initialSeatCount, IEnumerable ownerEmails); Task SendOrganizationAcceptedEmailAsync(Organization organization, string userIdentifier, IEnumerable adminEmails, 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 SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(string organizationName, string email); Task SendPasswordlessSignInAsync(string returnUrl, string token, string email); @@ -61,7 +60,6 @@ public interface IMailService Task SendLicenseExpiredAsync(IEnumerable emails, string? organizationName = null); Task SendNewDeviceLoggedInEmail(string email, string deviceType, 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 SendEmergencyAccessAcceptedEmailAsync(string granteeEmail, string email); Task SendEmergencyAccessConfirmedEmailAsync(string grantorName, string email); @@ -88,7 +86,6 @@ public interface IMailService Task SendFamiliesForEnterpriseRedeemedEmailsAsync(string familyUserEmail, string sponsorEmail); Task SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(string email, DateTime expirationDate); Task SendOTPEmailAsync(string email, string token); - Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable adminEmails, string organizationId, string domainName); Task SendUnclaimedOrganizationDomainEmailAsync(IEnumerable adminEmails, string organizationId, string domainName); Task SendSecretsManagerMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable ownerEmails); Task SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable ownerEmails); diff --git a/src/Core/Services/IUserService.cs b/src/Core/Services/IUserService.cs index 228b8543d7..e63b4e3b87 100644 --- a/src/Core/Services/IUserService.cs +++ b/src/Core/Services/IUserService.cs @@ -133,16 +133,11 @@ public interface IUserService /// verified domains of that organization, and the user is a member of it. /// The organization must be enabled and able to have verified domains. /// - /// - /// False if the Account Deprovisioning feature flag is disabled. - /// Task IsClaimedByAnyOrganizationAsync(Guid userId); /// /// Verify whether the new email domain meets the requirements for managed users. /// - /// - /// /// /// IdentityResult /// @@ -151,9 +146,6 @@ public interface IUserService /// /// Gets the organizations that manage the user. /// - /// - /// An empty collection if the Account Deprovisioning feature flag is disabled. - /// /// Task> GetOrganizationsClaimingUserAsync(Guid userId); } diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs index 315e180721..20f6e3a0ab 100644 --- a/src/Core/Services/Implementations/HandlebarsMailService.cs +++ b/src/Core/Services/Implementations/HandlebarsMailService.cs @@ -301,20 +301,6 @@ public class HandlebarsMailService : IMailService 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) { var message = CreateDefaultMessage($"You have been revoked from {organizationName}", email); @@ -532,20 +518,6 @@ public class HandlebarsMailService : IMailService 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) { var message = CreateDefaultMessage($"You have been revoked from {organizationName}", email); @@ -1137,19 +1109,6 @@ public class HandlebarsMailService : IMailService await _mailDeliveryService.SendEmailAsync(message); } - public async Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable 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 adminEmails, string organizationId, string domainName) { var message = CreateDefaultMessage("Domain not claimed", adminEmails); diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index 23617a0fcd..71661493ec 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -1336,11 +1336,6 @@ public class UserService : UserManager, IUserService, IDisposable public async Task> GetOrganizationsClaimingUserAsync(Guid userId) { - if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)) - { - return Enumerable.Empty(); - } - // Get all organizations that have verified the user's email domain. var organizationsWithVerifiedUserEmailDomain = await _organizationRepository.GetByVerifiedUserEmailDomainAsync(userId); @@ -1405,22 +1400,12 @@ public class UserService : UserManager, IUserService, IDisposable var removeOrgUserTasks = twoFactorPolicies.Select(async p => { var organization = await _organizationRepository.GetByIdAsync(p.OrganizationId); - if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)) - { - await _revokeNonCompliantOrganizationUserCommand.RevokeNonCompliantOrganizationUsersAsync( - new RevokeOrganizationUsersRequest( - p.OrganizationId, - [new OrganizationUserUserDetails { Id = p.OrganizationUserId, OrganizationId = p.OrganizationId }], - 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); - } - + await _revokeNonCompliantOrganizationUserCommand.RevokeNonCompliantOrganizationUsersAsync( + new RevokeOrganizationUsersRequest( + p.OrganizationId, + [new OrganizationUserUserDetails { Id = p.OrganizationUserId, OrganizationId = p.OrganizationId }], + new SystemUser(EventSystemUser.TwoFactorDisabled))); + await _mailService.SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(organization.DisplayName(), user.Email); }).ToArray(); await Task.WhenAll(removeOrgUserTasks); diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs index 83bc3ba7cf..26858911a8 100644 --- a/src/Core/Services/NoopImplementations/NoopMailService.cs +++ b/src/Core/Services/NoopImplementations/NoopMailService.cs @@ -80,11 +80,6 @@ public class NoopMailService : IMailService return Task.FromResult(0); } - public Task SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(string organizationName, string email) - { - return Task.FromResult(0); - } - public Task SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(string organizationName, string email) => Task.CompletedTask; @@ -155,11 +150,6 @@ public class NoopMailService : IMailService return Task.FromResult(0); } - public Task SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(string organizationName, string email) - { - return Task.FromResult(0); - } - public Task SendEmergencyAccessInviteEmailAsync(EmergencyAccess emergencyAccess, string name, string token) { return Task.FromResult(0); @@ -268,11 +258,6 @@ public class NoopMailService : IMailService return Task.FromResult(0); } - public Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable adminEmails, string organizationId, string domainName) - { - return Task.FromResult(0); - } - public Task SendUnclaimedOrganizationDomainEmailAsync(IEnumerable adminEmails, string organizationId, string domainName) { return Task.FromResult(0); diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs index 107b9cdfb1..de54a44bca 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs @@ -238,20 +238,13 @@ public class OrganizationUsersControllerTests await Assert.ThrowsAsync(() => sutProvider.Sut.Invite(organizationAbility.Id, model)); } - [Theory] - [BitAutoData(true)] - [BitAutoData(false)] + [Theory, BitAutoData] public async Task Get_ReturnsUser( - bool accountDeprovisioningEnabled, OrganizationUserUserDetails organizationUser, ICollection collections, SutProvider sutProvider) { organizationUser.Permissions = null; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(accountDeprovisioningEnabled); - sutProvider.GetDependency() .ManageUsers(organizationUser.OrganizationId) .Returns(true); @@ -267,8 +260,8 @@ public class OrganizationUsersControllerTests var response = await sutProvider.Sut.Get(organizationUser.Id, false); Assert.Equal(organizationUser.Id, response.Id); - Assert.Equal(accountDeprovisioningEnabled, response.ManagedByOrganization); - Assert.Equal(accountDeprovisioningEnabled, response.ClaimedByOrganization); + Assert.True(response.ManagedByOrganization); + Assert.True(response.ClaimedByOrganization); } [Theory] diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs index 867f8f8ec6..7d0a57ea45 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs @@ -140,7 +140,6 @@ public class OrganizationsControllerTests : IDisposable _currentContext.OrganizationUser(orgId).Returns(true); _ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig); _userService.GetUserByPrincipalAsync(Arg.Any()).Returns(user); - _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List { null }); var exception = await Assert.ThrowsAsync(() => _sut.Leave(orgId)); @@ -170,7 +169,6 @@ public class OrganizationsControllerTests : IDisposable _currentContext.OrganizationUser(orgId).Returns(true); _ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig); _userService.GetUserByPrincipalAsync(Arg.Any()).Returns(user); - _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List { { foundOrg } }); var exception = await Assert.ThrowsAsync(() => _sut.Leave(orgId)); @@ -205,7 +203,6 @@ public class OrganizationsControllerTests : IDisposable _currentContext.OrganizationUser(orgId).Returns(true); _ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig); _userService.GetUserByPrincipalAsync(Arg.Any()).Returns(user); - _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List()); await _sut.Leave(orgId); diff --git a/test/Api.Test/Auth/Controllers/AccountsControllerTests.cs b/test/Api.Test/Auth/Controllers/AccountsControllerTests.cs index c617f6c9a9..581a7e8f04 100644 --- a/test/Api.Test/Auth/Controllers/AccountsControllerTests.cs +++ b/test/Api.Test/Auth/Controllers/AccountsControllerTests.cs @@ -7,7 +7,6 @@ using Bit.Api.Auth.Models.Request.WebAuthn; using Bit.Api.KeyManagement.Validators; using Bit.Api.Tools.Models.Request; using Bit.Api.Vault.Models.Request; -using Bit.Core; using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Services; using Bit.Core.Auth.Entities; @@ -193,21 +192,6 @@ public class AccountsControllerTests : IDisposable 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] public async Task PostEmail_WhenNotAuthorized_ShouldThrownUnauthorizedAccessException() { @@ -537,12 +521,11 @@ public class AccountsControllerTests : IDisposable } [Fact] - public async Task Delete_WhenAccountDeprovisioningIsEnabled_WithUserManagedByAnOrganization_ThrowsBadRequestException() + public async Task Delete_WithUserManagedByAnOrganization_ThrowsBadRequestException() { var user = GenerateExampleUser(); ConfigureUserServiceToReturnValidPrincipalFor(user); ConfigureUserServiceToAcceptPasswordFor(user); - _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _userService.IsClaimedByAnyOrganizationAsync(user.Id).Returns(true); var result = await Assert.ThrowsAsync(() => _sut.Delete(new SecretVerificationRequestModel())); @@ -551,12 +534,11 @@ public class AccountsControllerTests : IDisposable } [Fact] - public async Task Delete_WhenAccountDeprovisioningIsEnabled_WithUserNotManagedByAnOrganization_ShouldSucceed() + public async Task Delete_WithUserNotManagedByAnOrganization_ShouldSucceed() { var user = GenerateExampleUser(); ConfigureUserServiceToReturnValidPrincipalFor(user); ConfigureUserServiceToAcceptPasswordFor(user); - _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _userService.IsClaimedByAnyOrganizationAsync(user.Id).Returns(false); _userService.DeleteAsync(user).Returns(IdentityResult.Success); diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs index daa560f3bc..b0774927e3 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs @@ -166,7 +166,7 @@ public class VerifyOrganizationDomainCommandTests } [Theory, BitAutoData] - public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeEnabled( + public async Task UserVerifyOrganizationDomainAsync_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeEnabled( OrganizationDomain domain, Guid userId, SutProvider sutProvider) { sutProvider.GetDependency() @@ -177,10 +177,6 @@ public class VerifyOrganizationDomainCommandTests .ResolveAsync(domain.DomainName, domain.Txt) .Returns(true); - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() .UserId.Returns(userId); @@ -196,33 +192,7 @@ public class VerifyOrganizationDomainCommandTests } [Theory, BitAutoData] - public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningDisabled_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeNotBeEnabled( - OrganizationDomain domain, SutProvider sutProvider) - { - sutProvider.GetDependency() - .GetClaimedDomainsByDomainNameAsync(domain.DomainName) - .Returns([]); - - sutProvider.GetDependency() - .ResolveAsync(domain.DomainName, domain.Txt) - .Returns(true); - - sutProvider.GetDependency() - .UserId.Returns(Guid.NewGuid()); - - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(false); - - _ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain); - - await sutProvider.GetDependency() - .DidNotReceive() - .SaveAsync(Arg.Any()); - } - - [Theory, BitAutoData] - public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldNotBeEnabled( + public async Task UserVerifyOrganizationDomainAsync_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldNotBeEnabled( OrganizationDomain domain, SutProvider sutProvider) { sutProvider.GetDependency() @@ -236,10 +206,6 @@ public class VerifyOrganizationDomainCommandTests sutProvider.GetDependency() .UserId.Returns(Guid.NewGuid()); - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - _ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain); await sutProvider.GetDependency() @@ -248,33 +214,7 @@ public class VerifyOrganizationDomainCommandTests } [Theory, BitAutoData] - public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningDisabled_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldBeNotBeEnabled( - OrganizationDomain domain, SutProvider sutProvider) - { - sutProvider.GetDependency() - .GetClaimedDomainsByDomainNameAsync(domain.DomainName) - .Returns([]); - - sutProvider.GetDependency() - .ResolveAsync(domain.DomainName, domain.Txt) - .Returns(false); - - sutProvider.GetDependency() - .UserId.Returns(Guid.NewGuid()); - - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - - _ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain); - - await sutProvider.GetDependency() - .DidNotReceive() - .SaveAsync(Arg.Any()); - } - - [Theory, BitAutoData] - public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsVerified_ThenEmailShouldBeSentToUsersWhoBelongToTheDomain( + public async Task UserVerifyOrganizationDomainAsync_WhenDomainIsVerified_ThenEmailShouldBeSentToUsersWhoBelongToTheDomain( ICollection organizationUsers, OrganizationDomain domain, Organization organization, @@ -306,10 +246,6 @@ public class VerifyOrganizationDomainCommandTests sutProvider.GetDependency() .UserId.Returns(Guid.NewGuid()); - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() .GetManyDetailsByOrganizationAsync(domain.OrganizationId) .Returns(mockedUsers); diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommandTests.cs index 3578706e47..c105c7a9ee 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommandTests.cs @@ -40,43 +40,6 @@ public class RemoveOrganizationUserCommandTests // Act await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId); - // Assert - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .GetUsersOrganizationClaimedStatusAsync(default, default); - await sutProvider.GetDependency() - .Received(1) - .DeleteAsync(organizationUser); - await sutProvider.GetDependency() - .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 sutProvider) - { - // Arrange - organizationUser.OrganizationId = deletingUser.OrganizationId; - - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() - .GetByIdAsync(organizationUser.Id) - .Returns(organizationUser); - sutProvider.GetDependency() - .GetByIdAsync(deletingUser.Id) - .Returns(deletingUser); - sutProvider.GetDependency() - .OrganizationOwner(deletingUser.OrganizationId) - .Returns(true); - - // Act - await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId); - // Assert await sutProvider.GetDependency() .Received(1) @@ -235,15 +198,12 @@ public class RemoveOrganizationUserCommandTests } [Theory, BitAutoData] - public async Task RemoveUserAsync_WithDeletingUserId_WithAccountDeprovisioningEnabled_WhenUserIsManaged_ThrowsException( + public async Task RemoveUserAsync_WithDeletingUserId_WhenUserIsManaged_ThrowsException( [OrganizationUser(status: OrganizationUserStatusType.Confirmed)] OrganizationUser orgUser, Guid deletingUserId, SutProvider sutProvider) { // Arrange - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); sutProvider.GetDependency() .GetByIdAsync(orgUser.Id) .Returns(orgUser); @@ -285,34 +245,6 @@ public class RemoveOrganizationUserCommandTests .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser); } - [Theory, BitAutoData] - public async Task RemoveUser_WithEventSystemUser_WithAccountDeprovisioningEnabled_Success( - [OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser, - EventSystemUser eventSystemUser, SutProvider sutProvider) - { - // Arrange - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() - .GetByIdAsync(organizationUser.Id) - .Returns(organizationUser); - - // Act - await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.Id, eventSystemUser); - - // Assert - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .GetUsersOrganizationClaimedStatusAsync(default, default); - await sutProvider.GetDependency() - .Received(1) - .DeleteAsync(organizationUser); - await sutProvider.GetDependency() - .Received(1) - .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser); - } - [Theory] [BitAutoData] public async Task RemoveUser_WithEventSystemUser_NotFound_ThrowsException( @@ -474,7 +406,6 @@ public class RemoveOrganizationUserCommandTests var sutProvider = SutProviderFactory(); var eventDate = sutProvider.GetDependency().GetUtcNow().UtcDateTime; orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId; - var organizationUsers = new[] { orgUser1, orgUser2 }; var organizationUserIds = organizationUsers.Select(u => u.Id); @@ -499,60 +430,6 @@ public class RemoveOrganizationUserCommandTests // Act 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() - .DidNotReceiveWithAnyArgs() - .GetUsersOrganizationClaimedStatusAsync(default, default); - await sutProvider.GetDependency() - .Received(1) - .DeleteManyAsync(Arg.Is>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id))); - await sutProvider.GetDependency() - .Received(1) - .LogOrganizationUserEventsAsync( - Arg.Is>(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().GetUtcNow().UtcDateTime; - orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId; - var organizationUsers = new[] { orgUser1, orgUser2 }; - var organizationUserIds = organizationUsers.Select(u => u.Id); - - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() - .GetManyAsync(default) - .ReturnsForAnyArgs(organizationUsers); - sutProvider.GetDependency() - .GetByIdAsync(deletingUser.Id) - .Returns(deletingUser); - sutProvider.GetDependency() - .HasConfirmedOwnersExceptAsync(deletingUser.OrganizationId, Arg.Any>()) - .Returns(true); - sutProvider.GetDependency() - .OrganizationOwner(deletingUser.OrganizationId) - .Returns(true); - sutProvider.GetDependency() - .GetUsersOrganizationClaimedStatusAsync( - deletingUser.OrganizationId, - Arg.Is>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id))) - .Returns(new Dictionary { { orgUser1.Id, false }, { orgUser2.Id, false } }); - - // Act - 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)); @@ -638,7 +515,7 @@ public class RemoveOrganizationUserCommandTests } [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 deletingUser, SutProvider sutProvider) @@ -646,10 +523,6 @@ public class RemoveOrganizationUserCommandTests // Arrange orgUser.OrganizationId = deletingUser.OrganizationId; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() .GetManyAsync(Arg.Is>(i => i.Contains(orgUser.Id))) .Returns(new[] { orgUser }); @@ -739,51 +612,6 @@ public class RemoveOrganizationUserCommandTests && 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().GetUtcNow().UtcDateTime; - orgUser1.OrganizationId = orgUser2.OrganizationId; - var organizationUsers = new[] { orgUser1, orgUser2 }; - var organizationUserIds = organizationUsers.Select(u => u.Id); - - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() - .GetManyAsync(default) - .ReturnsForAnyArgs(organizationUsers); - sutProvider.GetDependency() - .HasConfirmedOwnersExceptAsync(orgUser1.OrganizationId, Arg.Any>()) - .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() - .DidNotReceiveWithAnyArgs() - .GetUsersOrganizationClaimedStatusAsync(default, default); - await sutProvider.GetDependency() - .Received(1) - .DeleteManyAsync(Arg.Is>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id))); - await sutProvider.GetDependency() - .Received(1) - .LogOrganizationUserEventsAsync( - Arg.Is>( - i => i.First().OrganizationUser.Id == orgUser1.Id - && i.Last().OrganizationUser.Id == orgUser2.Id - && i.All(u => u.EventSystemUser == eventSystemUser - && u.DateTime == eventDate))); - } - [Theory, BitAutoData] public async Task RemoveUsers_WithEventSystemUser_WithMismatchingOrganizationId_ThrowsException( EventSystemUser eventSystemUser, diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidatorTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidatorTests.cs index 6048ed54d5..e982a67e46 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidatorTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidatorTests.cs @@ -122,9 +122,6 @@ public class SingleOrgPolicyValidatorTests sutProvider.GetDependency().UserId.Returns(savingUserId); sutProvider.GetDependency().GetByIdAsync(policyUpdate.OrganizationId).Returns(organization); - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() .RevokeNonCompliantOrganizationUsersAsync(Arg.Any()) .Returns(new CommandResult()); @@ -148,161 +145,4 @@ public class SingleOrgPolicyValidatorTests .Received(1) .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 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() - .GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId) - .Returns([compliantUser1, compliantUser2, nonCompliantUser]); - - var otherOrganizationUser = new OrganizationUser - { - Id = Guid.NewGuid(), - OrganizationId = new Guid(), - UserId = nonCompliantUserId, - Status = OrganizationUserStatusType.Confirmed - }; - - sutProvider.GetDependency() - .GetManyByManyUsersAsync(Arg.Is>(ids => ids.Contains(nonCompliantUserId))) - .Returns([otherOrganizationUser]); - - sutProvider.GetDependency().UserId.Returns(savingUserId); - sutProvider.GetDependency().GetByIdAsync(policyUpdate.OrganizationId).Returns(organization); - - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(false); - - sutProvider.GetDependency() - .RevokeNonCompliantOrganizationUsersAsync(Arg.Any()) - .Returns(new CommandResult()); - - await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy); - - await sutProvider.GetDependency() - .DidNotReceive() - .RemoveUserAsync(policyUpdate.OrganizationId, compliantUser1.Id, savingUserId); - await sutProvider.GetDependency() - .DidNotReceive() - .RemoveUserAsync(policyUpdate.OrganizationId, compliantUser2.Id, savingUserId); - await sutProvider.GetDependency() - .Received(1) - .RemoveUserAsync(policyUpdate.OrganizationId, nonCompliantUser.Id, savingUserId); - await sutProvider.GetDependency() - .DidNotReceive() - .SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(organization.DisplayName(), compliantUser1.Email); - await sutProvider.GetDependency() - .DidNotReceive() - .SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(organization.DisplayName(), compliantUser2.Email); - await sutProvider.GetDependency() - .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 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() - .GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId) - .Returns([compliantUser1, compliantUser2, nonCompliantUser]); - - var otherOrganizationUser = new OrganizationUser - { - OrganizationId = new Guid(), - UserId = nonCompliantUserId, - Status = OrganizationUserStatusType.Confirmed - }; - - sutProvider.GetDependency() - .GetManyByManyUsersAsync(Arg.Is>(ids => ids.Contains(nonCompliantUserId))) - .Returns([otherOrganizationUser]); - - sutProvider.GetDependency().UserId.Returns(savingUserId); - sutProvider.GetDependency().GetByIdAsync(policyUpdate.OrganizationId) - .Returns(organization); - - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); - - sutProvider.GetDependency() - .RevokeNonCompliantOrganizationUsersAsync(Arg.Any()) - .Returns(new CommandResult()); - - await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy); - - await sutProvider.GetDependency() - .Received() - .RevokeNonCompliantOrganizationUsersAsync(Arg.Any()); - } } diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidatorTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidatorTests.cs index e368f77699..6a97f6bc1e 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidatorTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidatorTests.cs @@ -6,7 +6,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators; using Bit.Core.AdminConsole.Utilities.Commands; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; -using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Data.Organizations.OrganizationUsers; @@ -24,7 +23,7 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Policies.PolicyValidat public class TwoFactorAuthenticationPolicyValidatorTests { [Theory, BitAutoData] - public async Task OnSaveSideEffectsAsync_RemovesNonCompliantUsers( + public async Task OnSaveSideEffectsAsync_GivenNonCompliantUsersWithoutMasterPassword_Throws( Organization organization, [PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate, [Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy, @@ -33,249 +32,6 @@ public class TwoFactorAuthenticationPolicyValidatorTests policy.OrganizationId = organization.Id = policyUpdate.OrganizationId; sutProvider.GetDependency().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() - .GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId) - .Returns(new List - { - orgUserDetailUserInvited, - orgUserDetailUserAcceptedWith2FA, - orgUserDetailUserAcceptedWithout2FA, - orgUserDetailAdmin - }); - - sutProvider.GetDependency() - .TwoFactorIsEnabledAsync(Arg.Any>()) - .Returns(new List<(OrganizationUserUserDetails user, bool hasTwoFactor)>() - { - (orgUserDetailUserInvited, false), - (orgUserDetailUserAcceptedWith2FA, true), - (orgUserDetailUserAcceptedWithout2FA, false), - (orgUserDetailAdmin, false), - }); - - var savingUserId = Guid.NewGuid(); - sutProvider.GetDependency().UserId.Returns(savingUserId); - - await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy); - - var removeOrganizationUserCommand = sutProvider.GetDependency(); - - await removeOrganizationUserCommand.Received() - .RemoveUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWithout2FA.Id, savingUserId); - await sutProvider.GetDependency().Received() - .SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWithout2FA.Email); - - await removeOrganizationUserCommand.DidNotReceive() - .RemoveUserAsync(policy.OrganizationId, orgUserDetailUserInvited.Id, savingUserId); - await sutProvider.GetDependency().DidNotReceive() - .SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserInvited.Email); - await removeOrganizationUserCommand.DidNotReceive() - .RemoveUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWith2FA.Id, savingUserId); - await sutProvider.GetDependency().DidNotReceive() - .SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWith2FA.Email); - await removeOrganizationUserCommand.DidNotReceive() - .RemoveUserAsync(policy.OrganizationId, orgUserDetailAdmin.Id, savingUserId); - await sutProvider.GetDependency().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 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() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(false); - - sutProvider.GetDependency() - .GetManyDetailsByOrganizationAsync(policy.OrganizationId) - .Returns(new List - { - orgUserDetailUserWith2FAAndMP, - orgUserDetailUserWith2FANoMP, - orgUserDetailUserWithout2FA, - orgUserDetailAdmin - }); - - sutProvider.GetDependency() - .TwoFactorIsEnabledAsync(Arg.Is>(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( - () => sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy)); - - Assert.Equal(TwoFactorAuthenticationPolicyValidator.NonCompliantMembersWillLoseAccessMessage, badRequestException.Message); - - await sutProvider.GetDependency().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 sutProvider) - { - policy.OrganizationId = organization.Id = policyUpdate.OrganizationId; - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - - sutProvider.GetDependency() - .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() - .GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId) - .Returns(new List - { - orgUserDetailUserAcceptedWithout2Fa - }); - - - sutProvider.GetDependency() - .TwoFactorIsEnabledAsync(Arg.Any>()) - .Returns(new List<(OrganizationUserUserDetails user, bool hasTwoFactor)>() - { - (orgUserDetailUserAcceptedWithout2Fa, false), - }); - - await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy); - - await sutProvider.GetDependency() - .DidNotReceive() - .RevokeNonCompliantOrganizationUsersAsync(Arg.Any()); - } - - [Theory, BitAutoData] - public async Task OnSaveSideEffectsAsync_GivenUpdateTo2faPolicy_WhenAccountProvisioningIsEnabledAndUserDoesNotHaveMasterPassword_ThenNonCompliantMembersErrorMessageWillReturn( - Organization organization, - [PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate, - [Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy, - SutProvider sutProvider) - { - policy.OrganizationId = organization.Id = policyUpdate.OrganizationId; - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - var orgUserDetailUserWithout2Fa = new OrganizationUserUserDetails { Id = Guid.NewGuid(), @@ -304,7 +60,7 @@ public class TwoFactorAuthenticationPolicyValidatorTests } [Theory, BitAutoData] - public async Task OnSaveSideEffectsAsync_WhenAccountProvisioningIsEnabledAndUserHasMasterPassword_ThenUserWillBeRevoked( + public async Task OnSaveSideEffectsAsync_RevokesNonCompliantUsers( Organization organization, [PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate, [Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy, @@ -313,10 +69,6 @@ public class TwoFactorAuthenticationPolicyValidatorTests policy.OrganizationId = organization.Id = policyUpdate.OrganizationId; sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - var orgUserDetailUserWithout2Fa = new OrganizationUserUserDetails { Id = Guid.NewGuid(), diff --git a/test/Core.Test/Services/UserServiceTests.cs b/test/Core.Test/Services/UserServiceTests.cs index d9bb2beaca..0458c7cdd9 100644 --- a/test/Core.Test/Services/UserServiceTests.cs +++ b/test/Core.Test/Services/UserServiceTests.cs @@ -343,28 +343,12 @@ public class UserServiceTests } [Theory, BitAutoData] - public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningDisabled_ReturnsFalse( - SutProvider sutProvider, Guid userId) - { - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(false); - - var result = await sutProvider.Sut.IsClaimedByAnyOrganizationAsync(userId); - Assert.False(result); - } - - [Theory, BitAutoData] - public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithManagingEnabledOrganization_ReturnsTrue( + public async Task IsClaimedByAnyOrganizationAsync_WithManagingEnabledOrganization_ReturnsTrue( SutProvider sutProvider, Guid userId, Organization organization) { organization.Enabled = true; organization.UseSso = true; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() .GetByVerifiedUserEmailDomainAsync(userId) .Returns(new[] { organization }); @@ -374,16 +358,12 @@ public class UserServiceTests } [Theory, BitAutoData] - public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithManagingDisabledOrganization_ReturnsFalse( + public async Task IsClaimedByAnyOrganizationAsync_WithManagingDisabledOrganization_ReturnsFalse( SutProvider sutProvider, Guid userId, Organization organization) { organization.Enabled = false; organization.UseSso = true; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() .GetByVerifiedUserEmailDomainAsync(userId) .Returns(new[] { organization }); @@ -393,16 +373,12 @@ public class UserServiceTests } [Theory, BitAutoData] - public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithOrganizationUseSsoFalse_ReturnsFalse( + public async Task IsClaimedByAnyOrganizationAsync_WithOrganizationUseSsoFalse_ReturnsFalse( SutProvider sutProvider, Guid userId, Organization organization) { organization.Enabled = true; organization.UseSso = false; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); - sutProvider.GetDependency() .GetByVerifiedUserEmailDomainAsync(userId) .Returns(new[] { organization }); @@ -412,100 +388,7 @@ public class UserServiceTests } [Theory, BitAutoData] - public async Task DisableTwoFactorProviderAsync_WhenOrganizationHas2FAPolicyEnabled_DisablingAllProviders_RemovesUserFromOrganizationAndSendsEmail( - SutProvider sutProvider, User user, Organization organization) - { - // Arrange - user.SetTwoFactorProviders(new Dictionary - { - [TwoFactorProviderType.Email] = new() { Enabled = true } - }); - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication) - .Returns( - [ - new OrganizationUserPolicyDetails - { - OrganizationId = organization.Id, - PolicyType = PolicyType.TwoFactorAuthentication, - PolicyEnabled = true - } - ]); - sutProvider.GetDependency() - .GetByIdAsync(organization.Id) - .Returns(organization); - var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary(), JsonHelpers.LegacyEnumKeyResolver); - - // Act - await sutProvider.Sut.DisableTwoFactorProviderAsync(user, TwoFactorProviderType.Email); - - // Assert - await sutProvider.GetDependency() - .Received(1) - .ReplaceAsync(Arg.Is(u => u.Id == user.Id && u.TwoFactorProviders == expectedSavedProviders)); - await sutProvider.GetDependency() - .Received(1) - .LogUserEventAsync(user.Id, EventType.User_Disabled2fa); - await sutProvider.GetDependency() - .Received(1) - .RemoveUserAsync(organization.Id, user.Id); - await sutProvider.GetDependency() - .Received(1) - .SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), user.Email); - } - - [Theory, BitAutoData] - public async Task DisableTwoFactorProviderAsync_WhenOrganizationHas2FAPolicyEnabled_UserHasOneProviderEnabled_DoesNotRemoveUserFromOrganization( - SutProvider sutProvider, User user, Organization organization) - { - // Arrange - user.SetTwoFactorProviders(new Dictionary - { - [TwoFactorProviderType.Email] = new() { Enabled = true }, - [TwoFactorProviderType.Remember] = new() { Enabled = true } - }); - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication) - .Returns( - [ - new OrganizationUserPolicyDetails - { - OrganizationId = organization.Id, - PolicyType = PolicyType.TwoFactorAuthentication, - PolicyEnabled = true - } - ]); - sutProvider.GetDependency() - .GetByIdAsync(organization.Id) - .Returns(organization); - sutProvider.GetDependency() - .TwoFactorIsEnabledAsync(user) - .Returns(true); - var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary - { - [TwoFactorProviderType.Remember] = new() { Enabled = true } - }, JsonHelpers.LegacyEnumKeyResolver); - - // Act - await sutProvider.Sut.DisableTwoFactorProviderAsync(user, TwoFactorProviderType.Email); - - // Assert - await sutProvider.GetDependency() - .Received(1) - .ReplaceAsync(Arg.Is(u => u.Id == user.Id && u.TwoFactorProviders == expectedSavedProviders)); - await sutProvider.GetDependency() - .Received(1) - .LogUserEventAsync(user.Id, EventType.User_Disabled2fa); - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .RemoveUserAsync(default, default); - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(default, default); - } - - [Theory, BitAutoData] - public async Task DisableTwoFactorProviderAsync_WithAccountDeprovisioningEnabled_WhenOrganizationHas2FAPolicyEnabled_DisablingAllProviders_RevokesUserAndSendsEmail( + public async Task DisableTwoFactorProviderAsync_WhenOrganizationHas2FAPolicyEnabled_DisablingAllProviders_RevokesUserAndSendsEmail( SutProvider sutProvider, User user, Organization organization1, Guid organizationUserId1, Organization organization2, Guid organizationUserId2) @@ -518,9 +401,6 @@ public class UserServiceTests organization1.Enabled = organization2.Enabled = true; organization1.UseSso = organization2.UseSso = true; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) - .Returns(true); sutProvider.GetDependency() .GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication) .Returns( @@ -583,7 +463,7 @@ public class UserServiceTests } [Theory, BitAutoData] - public async Task DisableTwoFactorProviderAsync_WithAccountDeprovisioningEnabled_UserHasOneProviderEnabled_DoesNotRemoveUserFromOrganization( + public async Task DisableTwoFactorProviderAsync_UserHasOneProviderEnabled_DoesNotRevokeUserFromOrganization( SutProvider sutProvider, User user, Organization organization) { // Arrange @@ -606,6 +486,9 @@ public class UserServiceTests sutProvider.GetDependency() .GetByIdAsync(organization.Id) .Returns(organization); + sutProvider.GetDependency() + .TwoFactorIsEnabledAsync(user) + .Returns(true); var expectedSavedProviders = JsonHelpers.LegacySerialize(new Dictionary { [TwoFactorProviderType.Remember] = new() { Enabled = true }