diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AcceptOrgUserCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AcceptOrgUserCommand.cs index 241c28e6954b..b660f2be7e3d 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AcceptOrgUserCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AcceptOrgUserCommand.cs @@ -1,13 +1,11 @@ // FIXME: Update this file to be null safe and then delete the line below #nullable disable -using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements.Errors; -using Bit.Core.AdminConsole.Services; using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; @@ -26,7 +24,6 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand { private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationRepository _organizationRepository; - private readonly IPolicyService _policyService; private readonly IMailService _mailService; private readonly IUserRepository _userRepository; private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery; @@ -40,7 +37,6 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand public AcceptOrgUserCommand( IOrganizationUserRepository organizationUserRepository, IOrganizationRepository organizationRepository, - IPolicyService policyService, IMailService mailService, IUserRepository userRepository, ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery, @@ -53,7 +49,6 @@ public AcceptOrgUserCommand( { _organizationUserRepository = organizationUserRepository; _organizationRepository = organizationRepository; - _policyService = policyService; _mailService = mailService; _userRepository = userRepository; _twoFactorIsEnabledQuery = twoFactorIsEnabledQuery; @@ -226,31 +221,16 @@ private async Task ValidateSingleOrganizationPolicyAsync(OrganizationUser orgUse private async Task ValidateTwoFactorAuthenticationPolicyAsync(User user, Guid organizationId) { - if (_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements)) + if (await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user)) { - if (await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user)) - { - // If the user has two-step login enabled, we skip checking the 2FA policy - return; - } - - var twoFactorPolicyRequirement = await _policyRequirementQuery.GetAsync(user.Id); - if (twoFactorPolicyRequirement.IsTwoFactorRequiredForOrganization(organizationId)) - { - throw new BadRequestException("You cannot join this organization until you enable two-step login on your user account."); - } - + // If the user has two-step login enabled, we skip checking the 2FA policy return; } - if (!await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user)) + var twoFactorPolicyRequirement = await _policyRequirementQuery.GetAsync(user.Id); + if (twoFactorPolicyRequirement.IsTwoFactorRequiredForOrganization(organizationId)) { - var invitedTwoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id, - PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited); - if (invitedTwoFactorPolicies.Any(p => p.OrganizationId == organizationId)) - { - throw new BadRequestException("You cannot join this organization until you enable two-step login on your user account."); - } + throw new BadRequestException("You cannot join this organization until you enable two-step login on your user account."); } } diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs index 1ffafc1c929e..985987380b2f 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs @@ -2,14 +2,12 @@ #nullable disable using Bit.Core.AdminConsole.Entities; -using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.OrganizationConfirmation; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements.Errors; -using Bit.Core.AdminConsole.Services; using Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Billing.Enums; @@ -31,7 +29,6 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery; private readonly IPushNotificationService _pushNotificationService; private readonly IPushRegistrationService _pushRegistrationService; - private readonly IPolicyService _policyService; private readonly IDeviceRepository _deviceRepository; private readonly IPolicyRequirementQuery _policyRequirementQuery; private readonly IFeatureService _featureService; @@ -48,7 +45,6 @@ public ConfirmOrganizationUserCommand( ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery, IPushNotificationService pushNotificationService, IPushRegistrationService pushRegistrationService, - IPolicyService policyService, IDeviceRepository deviceRepository, IPolicyRequirementQuery policyRequirementQuery, IFeatureService featureService, @@ -64,7 +60,6 @@ public ConfirmOrganizationUserCommand( _twoFactorIsEnabledQuery = twoFactorIsEnabledQuery; _pushNotificationService = pushNotificationService; _pushRegistrationService = pushRegistrationService; - _policyService = policyService; _deviceRepository = deviceRepository; _policyRequirementQuery = policyRequirementQuery; _featureService = featureService; @@ -237,26 +232,14 @@ private async Task CheckPoliciesAsync(Guid organizationId, User user, private async Task ValidateTwoFactorAuthenticationPolicyAsync(User user, Guid organizationId, bool userTwoFactorEnabled) { - if (_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements)) + if (userTwoFactorEnabled) { - if (userTwoFactorEnabled) - { - // If the user has two-step login enabled, we skip checking the 2FA policy - return; - } - - var twoFactorPolicyRequirement = await _policyRequirementQuery.GetAsync(user.Id); - if (twoFactorPolicyRequirement.IsTwoFactorRequiredForOrganization(organizationId)) - { - throw new BadRequestException("User does not have two-step login enabled."); - } - + // If the user has two-step login enabled, we skip checking the 2FA policy return; } - var orgRequiresTwoFactor = (await _policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication)) - .Any(p => p.OrganizationId == organizationId); - if (orgRequiresTwoFactor && !userTwoFactorEnabled) + var twoFactorPolicyRequirement = await _policyRequirementQuery.GetAsync(user.Id); + if (twoFactorPolicyRequirement.IsTwoFactorRequiredForOrganization(organizationId)) { throw new BadRequestException("User does not have two-step login enabled."); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RestoreUser/v1/RestoreOrganizationUserCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RestoreUser/v1/RestoreOrganizationUserCommand.cs index 9ec3e6c4528d..01cb4a203d26 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RestoreUser/v1/RestoreOrganizationUserCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RestoreUser/v1/RestoreOrganizationUserCommand.cs @@ -2,12 +2,10 @@ #nullable disable using Bit.Core.AdminConsole.Entities; -using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements.Errors; -using Bit.Core.AdminConsole.Services; using Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Billing.Enums; @@ -28,7 +26,6 @@ public class RestoreOrganizationUserCommand( IOrganizationUserRepository organizationUserRepository, IOrganizationRepository organizationRepository, ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery, - IPolicyService policyService, IUserRepository userRepository, IOrganizationService organizationService, IFeatureService featureService, @@ -373,14 +370,7 @@ private async Task CheckPoliciesBeforeRestoreAsync(OrganizationUser orgUser, boo private async Task IsTwoFactorRequiredForOrganizationAsync(Guid userId, Guid organizationId) { - if (featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements)) - { - var requirement = await policyRequirementQuery.GetAsync(userId); - return requirement.IsTwoFactorRequiredForOrganization(organizationId); - } - - var invitedTwoFactorPolicies = await policyService.GetPoliciesApplicableToUserAsync(userId, - PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Revoked); - return invitedTwoFactorPolicies.Any(p => p.OrganizationId == organizationId); + var requirement = await policyRequirementQuery.GetAsync(userId); + return requirement.IsTwoFactorRequiredForOrganization(organizationId); } } diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index 58f5cbb218fd..535f986496bc 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -10,7 +10,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; using Bit.Core.AdminConsole.Repositories; -using Bit.Core.AdminConsole.Services; using Bit.Core.Auth.Enums; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Billing.Licenses; @@ -30,7 +29,6 @@ using Bit.Core.Repositories; using Bit.Core.Settings; using Bit.Core.Utilities; -using Fido2NetLib; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Logging; @@ -54,11 +52,8 @@ public class UserService : UserManager, IUserService private readonly IEnumerable> _passwordValidators; private readonly ILicensingService _licenseService; private readonly IEventService _eventService; - private readonly IApplicationCacheService _applicationCacheService; private readonly IStripePaymentService _paymentService; private readonly IPolicyQuery _policyQuery; - private readonly IPolicyService _policyService; - private readonly IFido2 _fido2; private readonly ICurrentContext _currentContext; private readonly IGlobalSettings _globalSettings; private readonly IAcceptOrgUserCommand _acceptOrgUserCommand; @@ -90,11 +85,8 @@ public UserService( ILogger> logger, ILicensingService licenseService, IEventService eventService, - IApplicationCacheService applicationCacheService, IStripePaymentService paymentService, IPolicyQuery policyQuery, - IPolicyService policyService, - IFido2 fido2, ICurrentContext currentContext, IGlobalSettings globalSettings, IAcceptOrgUserCommand acceptOrgUserCommand, @@ -130,11 +122,8 @@ public UserService( _passwordValidators = passwordValidators; _licenseService = licenseService; _eventService = eventService; - _applicationCacheService = applicationCacheService; _paymentService = paymentService; _policyQuery = policyQuery; - _policyService = policyService; - _fido2 = fido2; _currentContext = currentContext; _globalSettings = globalSettings; _acceptOrgUserCommand = acceptOrgUserCommand; @@ -1058,51 +1047,38 @@ public void SetTwoFactorProvider(User user, TwoFactorProviderType type, bool set private async Task CheckPoliciesOnTwoFactorRemovalAsync(User user) { - if (_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements)) + var requirement = await _policyRequirementQuery.GetAsync(user.Id); + if (!requirement.OrganizationsRequiringTwoFactor.Any()) { - var requirement = await _policyRequirementQuery.GetAsync(user.Id); - if (!requirement.OrganizationsRequiringTwoFactor.Any()) - { - Logger.LogInformation("No organizations requiring two factor for user {userId}.", user.Id); - return; - } - - var organizationIds = requirement.OrganizationsRequiringTwoFactor.Select(o => o.OrganizationId).ToList(); - var organizations = await _organizationRepository.GetManyByIdsAsync(organizationIds); - var organizationLookup = organizations.ToDictionary(org => org.Id); - - var revokeOrgUserTasks = requirement.OrganizationsRequiringTwoFactor - .Where(o => organizationLookup.ContainsKey(o.OrganizationId)) - .Select(async o => - { - var organization = organizationLookup[o.OrganizationId]; - await _revokeNonCompliantOrganizationUserCommand.RevokeNonCompliantOrganizationUsersAsync( - new RevokeOrganizationUsersRequest( - o.OrganizationId, - [new OrganizationUserUserDetails { Id = o.OrganizationUserId, OrganizationId = o.OrganizationId }], - new SystemUser(EventSystemUser.TwoFactorDisabled))); - await _mailService.SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(organization.DisplayName(), user.Email); - }).ToArray(); - - await Task.WhenAll(revokeOrgUserTasks); - + Logger.LogInformation("No organizations requiring two factor for user {userId}.", user.Id); return; } - var twoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication); + var organizationIds = requirement.OrganizationsRequiringTwoFactor.Select(o => o.OrganizationId).ToList(); + var organizations = await _organizationRepository.GetManyByIdsAsync(organizationIds); + var organizationLookup = organizations.ToDictionary(org => org.Id); - var legacyRevokeOrgUserTasks = twoFactorPolicies.Select(async p => - { - var organization = await _organizationRepository.GetByIdAsync(p.OrganizationId); - 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(legacyRevokeOrgUserTasks); + var revokeOrgUserTasks = requirement.OrganizationsRequiringTwoFactor + .Where(o => organizationLookup.ContainsKey(o.OrganizationId)) + .Select(async o => + { + var organization = organizationLookup[o.OrganizationId]; + await _revokeNonCompliantOrganizationUserCommand.RevokeNonCompliantOrganizationUsersAsync( + new RevokeOrganizationUsersRequest( + o.OrganizationId, + [ + new OrganizationUserUserDetails + { + Id = o.OrganizationUserId, + OrganizationId = o.OrganizationId + } + ], + new SystemUser(EventSystemUser.TwoFactorDisabled))); + await _mailService.SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(organization.DisplayName(), + user.Email); + }).ToArray(); + + await Task.WhenAll(revokeOrgUserTasks); } public async Task RotateApiKeyAsync(User user) diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/AcceptOrgUserCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/AcceptOrgUserCommandTests.cs index 5cf660b90247..bbcf80084693 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/AcceptOrgUserCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/AcceptOrgUserCommandTests.cs @@ -6,7 +6,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Services; using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; @@ -112,39 +111,9 @@ public async Task AcceptOrgUser_OrgUserStatusIsNotInvited_ThrowsBadRequest( public async Task AcceptOrgUserAsync_UserWithout2FAJoining2FARequiredOrg_ThrowsBadRequest( SutProvider sutProvider, User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails) - { - // Arrange - SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails); - - // Organization they are trying to join requires 2FA - var twoFactorPolicy = new OrganizationUserPolicyDetails { OrganizationId = orgUser.OrganizationId }; - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication, - OrganizationUserStatusType.Invited) - .Returns(Task.FromResult>( - new List { twoFactorPolicy })); - - // Act & Assert - var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService)); - - Assert.Equal("You cannot join this organization until you enable two-step login on your user account.", - exception.Message); - } - - [Theory] - [BitAutoData] - public async Task AcceptOrgUserAsync_WithPolicyRequirementsEnabled_UserWithout2FAJoining2FARequiredOrg_ThrowsBadRequest( - SutProvider sutProvider, - User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails) { SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails); - // Enable the PolicyRequirements feature flag for the new 2FA path - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); - // No SingleOrg policy sutProvider.GetDependency() .GetAsync(user.Id) @@ -172,7 +141,7 @@ public async Task AcceptOrgUserAsync_WithPolicyRequirementsEnabled_UserWithout2F [Theory] [BitAutoData] - public async Task AcceptOrgUserAsync_WithPolicyRequirementsEnabled_UserWith2FAJoining2FARequiredOrg_Succeeds( + public async Task AcceptOrgUserAsync_UserWith2FAJoining2FARequiredOrg_Succeeds( SutProvider sutProvider, User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails) { @@ -210,7 +179,7 @@ await sutProvider.GetDependency() [Theory] [BitAutoData] - public async Task AcceptOrgUserAsync_WithPolicyRequirementsEnabled_UserJoiningOrgWithout2FARequirement_Succeeds( + public async Task AcceptOrgUserAsync_UserJoiningOrgWithout2FARequirement_Succeeds( SutProvider sutProvider, User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails) { @@ -908,11 +877,6 @@ private static void SetupCommonAcceptOrgUserMocks(SutProvider().GetPoliciesApplicableToUserAsync(user.Id, - PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited) - .Returns([]); - // Provide at least 1 admin to test email functionality sutProvider.GetDependency() .GetManyByMinimumRoleAsync(orgUser.OrganizationId, OrganizationUserType.Admin) @@ -928,6 +892,11 @@ private static void SetupCommonAcceptOrgUserMocks(SutProvider(Arg.Any()) .Returns(new SingleOrganizationPolicyRequirement([])); + // No 2FA policy by default + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Auto-confirm enforcement query returns valid by default (no restrictions) var request = new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser], user); diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommandTests.cs index 08c674bd37bd..92bb6ac3e94e 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommandTests.cs @@ -7,18 +7,15 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Services; using Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Billing.Enums; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Platform.Push; using Bit.Core.Repositories; using Bit.Core.Services; -using Bit.Core.Test.AdminConsole.AutoFixture; using Bit.Core.Test.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.Test.AutoFixture.OrganizationUserFixtures; using Bit.Test.Common.AutoFixture; @@ -145,6 +142,10 @@ public async Task ConfirmUserAsync_ToNonFree_WithExistingFreeAdminOrOwner_Succee .GetAsync(user.Id) .Returns(new SingleOrganizationPolicyRequirement([])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id); await sutProvider.GetDependency().Received(1).LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Confirmed); @@ -159,66 +160,6 @@ await sutProvider.GetDependency() } - [Theory, BitAutoData] - public async Task ConfirmUserAsync_WithTwoFactorPolicyAndTwoFactorDisabled_ThrowsBadRequestException(Organization org, OrganizationUser confirmingUser, - [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user, - OrganizationUser orgUserAnotherOrg, - [OrganizationUserPolicyDetails(PolicyType.TwoFactorAuthentication)] OrganizationUserPolicyDetails twoFactorPolicy, - string key, SutProvider sutProvider) - { - var organizationUserRepository = sutProvider.GetDependency(); - var organizationRepository = sutProvider.GetDependency(); - var userRepository = sutProvider.GetDependency(); - var policyService = sutProvider.GetDependency(); - var twoFactorIsEnabledQuery = sutProvider.GetDependency(); - - org.PlanType = PlanType.EnterpriseAnnually; - orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id; - orgUser.UserId = orgUserAnotherOrg.UserId = user.Id; - organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser }); - organizationUserRepository.GetManyByManyUsersAsync(default).ReturnsForAnyArgs(new[] { orgUserAnotherOrg }); - organizationRepository.GetByIdAsync(org.Id).Returns(org); - userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user }); - twoFactorPolicy.OrganizationId = org.Id; - policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication).Returns(new[] { twoFactorPolicy }); - twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(Arg.Is>(ids => ids.Contains(user.Id))) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (user.Id, false) }); - - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id)); - Assert.Contains("User does not have two-step login enabled.", exception.Message); - } - - [Theory, BitAutoData] - public async Task ConfirmUserAsync_WithTwoFactorPolicyAndTwoFactorEnabled_Succeeds(Organization org, OrganizationUser confirmingUser, - [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user, - [OrganizationUserPolicyDetails(PolicyType.TwoFactorAuthentication)] OrganizationUserPolicyDetails twoFactorPolicy, - string key, SutProvider sutProvider) - { - var organizationUserRepository = sutProvider.GetDependency(); - var organizationRepository = sutProvider.GetDependency(); - var userRepository = sutProvider.GetDependency(); - var policyService = sutProvider.GetDependency(); - var twoFactorIsEnabledQuery = sutProvider.GetDependency(); - - org.PlanType = PlanType.EnterpriseAnnually; - orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id; - orgUser.UserId = user.Id; - organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser }); - organizationRepository.GetByIdAsync(org.Id).Returns(org); - userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user }); - twoFactorPolicy.OrganizationId = org.Id; - policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication).Returns(new[] { twoFactorPolicy }); - twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(Arg.Is>(ids => ids.Contains(user.Id))) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (user.Id, true) }); - - sutProvider.GetDependency() - .GetAsync(user.Id) - .Returns(new SingleOrganizationPolicyRequirement([])); - - await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id); - } - [Theory, BitAutoData] public async Task ConfirmUserAsync_WithSingleOrgPolicyFromConfirmingOrg_ThrowsBadRequest( Organization org, OrganizationUser confirmingUser, @@ -333,7 +274,7 @@ public async Task ConfirmUserAsync_NoSingleOrgPolicy_Succeeds( } [Theory, BitAutoData] - public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorRequired_ThrowsBadRequestException( + public async Task ConfirmUserAsync_WithTwoFactorRequired_ThrowsBadRequestException( Organization org, OrganizationUser confirmingUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user, SutProvider sutProvider) @@ -341,7 +282,6 @@ public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorReq var organizationUserRepository = sutProvider.GetDependency(); var organizationRepository = sutProvider.GetDependency(); var userRepository = sutProvider.GetDependency(); - var featureService = sutProvider.GetDependency(); var policyRequirementQuery = sutProvider.GetDependency(); var twoFactorIsEnabledQuery = sutProvider.GetDependency(); @@ -351,7 +291,6 @@ public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorReq organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser }); organizationRepository.GetByIdAsync(org.Id).Returns(org); userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user }); - featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(true); policyRequirementQuery.GetAsync(user.Id) .Returns(new RequireTwoFactorPolicyRequirement( [ @@ -371,7 +310,7 @@ public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorReq } [Theory, BitAutoData] - public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorNotRequired_Succeeds( + public async Task ConfirmUserAsync_WithTwoFactorNotRequired_Succeeds( Organization org, OrganizationUser confirmingUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user, SutProvider sutProvider) @@ -379,7 +318,6 @@ public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorNot var organizationUserRepository = sutProvider.GetDependency(); var organizationRepository = sutProvider.GetDependency(); var userRepository = sutProvider.GetDependency(); - var featureService = sutProvider.GetDependency(); var policyRequirementQuery = sutProvider.GetDependency(); var twoFactorIsEnabledQuery = sutProvider.GetDependency(); @@ -390,7 +328,6 @@ public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorNot organizationUserRepository.GetManyByManyUsersAsync(default).ReturnsForAnyArgs(new[] { orgUser }); organizationRepository.GetByIdAsync(org.Id).Returns(org); userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user }); - featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(true); policyRequirementQuery.GetAsync(user.Id) .Returns(new RequireTwoFactorPolicyRequirement( [ @@ -414,7 +351,7 @@ public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorNot } [Theory, BitAutoData] - public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorEnabled_Succeeds( + public async Task ConfirmUserAsync_WithTwoFactorEnabled_Succeeds( Organization org, OrganizationUser confirmingUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user, SutProvider sutProvider) @@ -422,7 +359,6 @@ public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorEna var organizationUserRepository = sutProvider.GetDependency(); var organizationRepository = sutProvider.GetDependency(); var userRepository = sutProvider.GetDependency(); - var featureService = sutProvider.GetDependency(); var policyRequirementQuery = sutProvider.GetDependency(); var twoFactorIsEnabledQuery = sutProvider.GetDependency(); @@ -433,7 +369,6 @@ public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorEna organizationUserRepository.GetManyByManyUsersAsync(default).ReturnsForAnyArgs(new[] { orgUser }); organizationRepository.GetByIdAsync(org.Id).Returns(org); userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user }); - featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(true); policyRequirementQuery.GetAsync(user.Id) .Returns(new RequireTwoFactorPolicyRequirement( [ @@ -488,6 +423,10 @@ public async Task ConfirmUserAsync_WithOrganizationDataOwnershipPolicyApplicable .GetAsync(user.Id) .Returns(new SingleOrganizationPolicyRequirement([])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, collectionName); await sutProvider.GetDependency() @@ -516,6 +455,10 @@ public async Task ConfirmUserAsync_WithOrganizationDataOwnershipPolicyApplicable .GetAsync(user.Id) .Returns(new SingleOrganizationPolicyRequirement([])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, ""); await sutProvider.GetDependency() @@ -546,6 +489,10 @@ public async Task ConfirmUserAsync_WithOrganizationDataOwnershipPolicyNotApplica .GetAsync(orgUser.UserId!.Value) .Returns(new SingleOrganizationPolicyRequirement([])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, collectionName); await sutProvider.GetDependency() @@ -587,6 +534,10 @@ public async Task ConfirmUserAsync_WithAutoConfirmEnabledAndUserBelongsToAnother new AutomaticUserConfirmationPolicyEnforcementRequest(orgUser.Id, [orgUser, otherOrgUser], user), new UserCannotBelongToAnotherOrganization())); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id)); @@ -631,6 +582,10 @@ public async Task ConfirmUserAsync_WithAutoConfirmEnabledForOtherOrg_ThrowsBadRe new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser, otherOrgUser], user), new OtherOrganizationDoesNotAllowOtherMembership())); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act & Assert var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id)); @@ -674,6 +629,10 @@ public async Task ConfirmUserAsync_WithAutoConfirmEnabledAndUserIsProvider_Throw new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser], user), new ProviderUsersCannotJoin())); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act & Assert var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id)); @@ -719,6 +678,10 @@ public async Task ConfirmUserAsync_WithAutoConfirmNotApplicable_Succeeds( .GetAsync(user.Id) .Returns(new SingleOrganizationPolicyRequirement([])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id); @@ -764,6 +727,10 @@ public async Task ConfirmUserAsync_WithAutoConfirmPolicyEnabled_DeletesEmergency .IsCompliantAsync(Arg.Any(), Arg.Any()) .Returns(Valid(new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser], user))); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id); @@ -808,6 +775,10 @@ public async Task ConfirmUserAsync_WithAutoConfirmPolicyNotEnabled_DoesNotDelete .IsCompliantAsync(Arg.Any(), Arg.Any()) .Returns(Valid(new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser], user))); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id); @@ -822,7 +793,6 @@ public async Task ConfirmUserAsync_WithAutoConfirmValidationBeforeSingleOrgPolic Organization org, OrganizationUser confirmingUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user, OrganizationUser otherOrgUser, - [OrganizationUserPolicyDetails(PolicyType.SingleOrg)] OrganizationUserPolicyDetails singleOrgPolicy, string key, SutProvider sutProvider) { // Arrange - Setup conditions that would fail BOTH auto-confirm AND single org policy @@ -844,11 +814,6 @@ public async Task ConfirmUserAsync_WithAutoConfirmValidationBeforeSingleOrgPolic .IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers) .Returns(true); - singleOrgPolicy.OrganizationId = org.Id; - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg) - .Returns([singleOrgPolicy]); - sutProvider.GetDependency() .GetAsync(user.Id) .Returns(new AutomaticUserConfirmationPolicyRequirement([new PolicyDetails { OrganizationId = org.Id }])); @@ -859,6 +824,10 @@ public async Task ConfirmUserAsync_WithAutoConfirmValidationBeforeSingleOrgPolic new AutomaticUserConfirmationPolicyEnforcementRequest(org.Id, [orgUser, otherOrgUser], user), new UserCannotBelongToAnotherOrganization())); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act & Assert var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id)); @@ -922,6 +891,10 @@ public async Task ConfirmUsersAsync_WithAutoConfirmEnabled_MixedResults( .GetAsync(Arg.Any()) .Returns(new SingleOrganizationPolicyRequirement([])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + var keys = orgUsers.ToDictionary(ou => ou.Id, _ => key); // Act @@ -967,6 +940,10 @@ public async Task ConfirmUserAsync_UseMyItemsDisabled_DoesNotCreateDefaultCollec .GetAsync(orgUser.UserId!.Value) .Returns(new SingleOrganizationPolicyRequirement([])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, collectionName); @@ -1011,6 +988,10 @@ public async Task ConfirmUserAsync_UseMyItemsEnabled_CreatesDefaultCollection( .GetAsync(orgUser.UserId!.Value) .Returns(new SingleOrganizationPolicyRequirement([])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, collectionName); @@ -1053,6 +1034,10 @@ public async Task ConfirmUsersAsync_UseMyItemsDisabled_DoesNotCreateDefaultColle .GetAsync(Arg.Any()) .Returns(new SingleOrganizationPolicyRequirement([])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act await sutProvider.Sut.ConfirmUsersAsync(organization.Id, keys, confirmingUser.Id, collectionName); @@ -1117,6 +1102,10 @@ public async Task ConfirmUsersAsync_UseMyItemsEnabled_CreatesDefaultCollections( .GetAsync(Arg.Any()) .Returns(new SingleOrganizationPolicyRequirement([])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act await sutProvider.Sut.ConfirmUsersAsync(organization.Id, keys, confirmingUser.Id, collectionName); diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RestoreUser/RestoreOrganizationUserCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RestoreUser/RestoreOrganizationUserCommandTests.cs index 37793f06eb24..80d45cd148f1 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RestoreUser/RestoreOrganizationUserCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/RestoreUser/RestoreOrganizationUserCommandTests.cs @@ -6,7 +6,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Services; using Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Billing.Enums; @@ -161,60 +160,12 @@ public async Task RestoreUser_With2FAPolicyEnabled_WithoutUser2FAConfigured_Fail SutProvider sutProvider) { organizationUser.Email = null; - - sutProvider.GetDependency() - .TwoFactorIsEnabledAsync(Arg.Is>(i => i.Contains(organizationUser.UserId.Value))) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, false) }); - sutProvider.GetDependency() - .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts - { - Sponsored = 0, - Users = 1 - }); - RestoreUser_Setup(organization, owner, organizationUser, sutProvider); - - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any()) - .Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } }); - - var user = new User(); - user.Email = "test@bitwarden.com"; - sutProvider.GetDependency().GetByIdAsync(organizationUser.UserId.Value).Returns(user); - - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null)); - - Assert.Contains("test@bitwarden.com is not compliant with the two-step login policy", exception.Message.ToLowerInvariant()); - - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .RestoreAsync(Arg.Any(), Arg.Any()); - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .PushSyncOrgKeysAsync(Arg.Any()); - } - - [Theory, BitAutoData] - public async Task RestoreUser_WithPolicyRequirementsEnabled_With2FAPolicyEnabled_WithoutUser2FAConfigured_Fails( - Organization organization, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, - [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, - SutProvider sutProvider) - { - organizationUser.Email = null; sutProvider.GetDependency() .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts { Sponsored = 0, Users = 1 }); - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); - sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Is>(i => i.Contains(organizationUser.UserId.Value))) .Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, false) }); @@ -264,42 +215,6 @@ public async Task RestoreUser_With2FAPolicyEnabled_WithUser2FAConfigured_Success SutProvider sutProvider) { organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke - RestoreUser_Setup(organization, owner, organizationUser, sutProvider); - - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any()) - .Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } }); - sutProvider.GetDependency() - .TwoFactorIsEnabledAsync(Arg.Is>(i => i.Contains(organizationUser.UserId.Value))) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, true) }); - sutProvider.GetDependency() - .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts - { - Sponsored = 0, - Users = 1 - }); - await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null); - - await sutProvider.GetDependency() - .Received(1) - .RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Confirmed); - await sutProvider.GetDependency() - .Received(1) - .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored); - } - - [Theory, BitAutoData] - public async Task RestoreUser_WithPolicyRequirementsEnabled_With2FAPolicyEnabled_WithUser2FAConfigured_Success( - Organization organization, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, - [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, - SutProvider sutProvider) - { - organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke - - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); RestoreUser_Setup(organization, owner, organizationUser, sutProvider); @@ -332,7 +247,7 @@ await sutProvider.GetDependency() } [Theory, BitAutoData] - public async Task RestoreUser_WithPolicyRequirementsEnabled_WithSingleOrgPolicyEnabled_And_2FA_Policy_Fails( + public async Task RestoreUser_WithSingleOrgPolicyEnabled_And_2FA_Policy_Fails( Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, @@ -343,10 +258,6 @@ public async Task RestoreUser_WithPolicyRequirementsEnabled_WithSingleOrgPolicyE secondOrganizationUser.UserId = organizationUser.UserId; RestoreUser_Setup(organization, owner, organizationUser, sutProvider); - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); - sutProvider.GetDependency() .GetManyByUserAsync(organizationUser.UserId.Value) .Returns(new[] { organizationUser, secondOrganizationUser }); @@ -480,40 +391,6 @@ await sutProvider.GetDependency() .RestoreAsync(Arg.Any(), Arg.Any()); } - [Theory, BitAutoData] - public async Task RestoreUser_vNext_With2FAPolicyEnabled_WithUser2FAConfigured_Success( - Organization organization, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, - [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, - SutProvider sutProvider) - { - organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke - RestoreUser_Setup(organization, owner, organizationUser, sutProvider); - sutProvider.GetDependency() - .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts - { - Sponsored = 0, - Users = 1 - }); - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any()) - .Returns([new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } - ]); - - sutProvider.GetDependency() - .TwoFactorIsEnabledAsync(Arg.Is>(i => i.Contains(organizationUser.UserId.Value))) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUser.UserId.Value, true) }); - - await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, null); - - await sutProvider.GetDependency() - .Received(1) - .RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Confirmed); - await sutProvider.GetDependency() - .Received(1) - .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored); - } - [Theory, BitAutoData] public async Task RestoreUser_WhenUserOwningAnotherFreeOrganization_ThenRestoreUserFails( Organization organization, @@ -545,10 +422,20 @@ public async Task RestoreUser_WhenUserOwningAnotherFreeOrganization_ThenRestoreU .GetManyByUserIdAsync(organizationUser.UserId.Value) .Returns([otherOrganization]); - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any()) - .Returns([new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } - ]); + sutProvider.GetDependency() + .GetAsync(organizationUser.UserId.Value) + .Returns(new RequireTwoFactorPolicyRequirement( + [ + new PolicyDetails + { + OrganizationId = organizationUser.OrganizationId, + OrganizationUserStatus = OrganizationUserStatusType.Revoked, + PolicyType = PolicyType.TwoFactorAuthentication + } + ])); + sutProvider.GetDependency() + .GetAsync(organizationUser.UserId.Value) + .Returns(new SingleOrganizationPolicyRequirement([])); sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Is>(i => i.Contains(organizationUser.UserId.Value))) @@ -592,16 +479,20 @@ public async Task RestoreUser_WhenUserOwningAnotherFreeOrganizationAndIsOnlyAUse .GetManyByUserIdAsync(organizationUser.UserId.Value) .Returns([otherOrganization]); - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, - Arg.Any()) - .Returns([ - new OrganizationUserPolicyDetails + sutProvider.GetDependency() + .GetAsync(organizationUser.UserId.Value) + .Returns(new RequireTwoFactorPolicyRequirement( + [ + new PolicyDetails { OrganizationId = organizationUser.OrganizationId, + OrganizationUserStatus = OrganizationUserStatusType.Revoked, PolicyType = PolicyType.TwoFactorAuthentication } - ]); + ])); + sutProvider.GetDependency() + .GetAsync(organizationUser.UserId.Value) + .Returns(new SingleOrganizationPolicyRequirement([])); sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Is>(i => i.Contains(organizationUser.UserId.Value))) @@ -648,16 +539,20 @@ public async Task RestoreUser_WhenUserOwningAnotherFreeOrganizationAndCurrentOrg .GetManyByUserIdAsync(organizationUser.UserId.Value) .Returns([otherOrganization]); - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, - Arg.Any()) - .Returns([ - new OrganizationUserPolicyDetails + sutProvider.GetDependency() + .GetAsync(organizationUser.UserId.Value) + .Returns(new RequireTwoFactorPolicyRequirement( + [ + new PolicyDetails { OrganizationId = organizationUser.OrganizationId, + OrganizationUserStatus = OrganizationUserStatusType.Revoked, PolicyType = PolicyType.TwoFactorAuthentication } - ]); + ])); + sutProvider.GetDependency() + .GetAsync(organizationUser.UserId.Value) + .Returns(new SingleOrganizationPolicyRequirement([])); sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Is>(i => i.Contains(organizationUser.UserId.Value))) @@ -873,76 +768,9 @@ public async Task RestoreUsers_With2FAPolicy_BlocksNonCompliantUser(Organization { // Arrange RestoreUser_Setup(organization, owner, orgUser1, sutProvider); - var organizationUserRepository = sutProvider.GetDependency(); - var userRepository = sutProvider.GetDependency(); - var policyService = sutProvider.GetDependency(); - var userService = Substitute.For(); - - orgUser1.Email = orgUser2.Email = null; - orgUser3.UserId = null; - orgUser3.Key = null; - orgUser1.OrganizationId = orgUser2.OrganizationId = orgUser3.OrganizationId = organization.Id; - organizationUserRepository - .GetManyAsync(Arg.Is>(ids => ids.Contains(orgUser1.Id) && ids.Contains(orgUser2.Id) && ids.Contains(orgUser3.Id))) - .Returns(new[] { orgUser1, orgUser2, orgUser3 }); - sutProvider.GetDependency() - .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts - { - Sponsored = 0, - Users = 1 - }); - userRepository.GetByIdAsync(orgUser2.UserId!.Value).Returns(new User { Email = "test@example.com" }); - - // Setup 2FA policy - policyService.GetPoliciesApplicableToUserAsync(Arg.Any(), PolicyType.TwoFactorAuthentication, Arg.Any()) - .Returns([new OrganizationUserPolicyDetails { OrganizationId = organization.Id, PolicyType = PolicyType.TwoFactorAuthentication }]); - - // User1 has 2FA, User2 doesn't - sutProvider.GetDependency() - .TwoFactorIsEnabledAsync(Arg.Is>(ids => ids.Contains(orgUser1.UserId!.Value) && ids.Contains(orgUser2.UserId!.Value))) - .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> - { - (orgUser1.UserId!.Value, true), - (orgUser2.UserId!.Value, false) - }); - - // Act - var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id, orgUser2.Id, orgUser3.Id], owner.Id, userService, null); - - // Assert - Assert.Equal(3, result.Count); - Assert.Empty(result[0].Item2); // First user should succeed - Assert.Contains("two-step login", result[1].Item2); // Second user should fail - Assert.Empty(result[2].Item2); // Third user should succeed - await organizationUserRepository - .Received(1) - .RestoreAsync(orgUser1.Id, OrganizationUserStatusType.Confirmed); - await organizationUserRepository - .DidNotReceive() - .RestoreAsync(orgUser2.Id, Arg.Any()); - await organizationUserRepository - .Received(1) - .RestoreAsync(orgUser3.Id, OrganizationUserStatusType.Invited); - } - - [Theory, BitAutoData] - public async Task RestoreUsers_WithPolicyRequirementsEnabled_With2FAPolicy_BlocksNonCompliantUser(Organization organization, - [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, - [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser1, - [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser2, - [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser3, - SutProvider sutProvider) - { - // Arrange - RestoreUser_Setup(organization, owner, orgUser1, sutProvider); - - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); var organizationUserRepository = sutProvider.GetDependency(); var userRepository = sutProvider.GetDependency(); - var policyService = sutProvider.GetDependency(); var userService = Substitute.For(); orgUser1.Email = orgUser2.Email = null; @@ -1015,7 +843,6 @@ public async Task RestoreUsers_UserOwnsAnotherFreeOrganization_BlocksOwnerUserFr RestoreUser_Setup(organization, owner, orgUser1, sutProvider); var organizationUserRepository = sutProvider.GetDependency(); var userRepository = sutProvider.GetDependency(); - var policyService = sutProvider.GetDependency(); var userService = Substitute.For(); orgUser1.Email = orgUser2.Email = null; @@ -1046,10 +873,21 @@ public async Task RestoreUsers_UserOwnsAnotherFreeOrganization_BlocksOwnerUserFr .GetManyByIdsAsync(Arg.Is>(ids => ids.Contains(orgUserFromOtherOrg.OrganizationId))) .Returns([otherOrganization]); - // Setup 2FA policy - policyService.GetPoliciesApplicableToUserAsync(Arg.Any(), PolicyType.TwoFactorAuthentication, Arg.Any()) - .Returns([new OrganizationUserPolicyDetails { OrganizationId = organization.Id, PolicyType = PolicyType.TwoFactorAuthentication }]); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement( + [ + new PolicyDetails + { + OrganizationId = organization.Id, + OrganizationUserStatus = OrganizationUserStatusType.Revoked, + PolicyType = PolicyType.TwoFactorAuthentication + } + ])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new SingleOrganizationPolicyRequirement([])); // User1 has 2FA, User2 doesn't sutProvider.GetDependency() @@ -1084,7 +922,6 @@ public async Task RestoreUsers_UserOwnsAnotherFreeOrganizationButReactivatingOrg RestoreUser_Setup(organization, owner, orgUser1, sutProvider); var organizationUserRepository = sutProvider.GetDependency(); - var policyService = sutProvider.GetDependency(); var userService = Substitute.For(); orgUser1.OrganizationId = organization.Id; @@ -1111,12 +948,23 @@ public async Task RestoreUsers_UserOwnsAnotherFreeOrganizationButReactivatingOrg .GetManyByIdsAsync(Arg.Is>(ids => ids.Contains(orgUserFromOtherOrg.OrganizationId))) .Returns([otherOrganization]); - // Setup 2FA policy - policyService.GetPoliciesApplicableToUserAsync(Arg.Any(), PolicyType.TwoFactorAuthentication, Arg.Any()) - .Returns([new OrganizationUserPolicyDetails { OrganizationId = organization.Id, PolicyType = PolicyType.TwoFactorAuthentication }]); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement( + [ + new PolicyDetails + { + OrganizationId = organization.Id, + OrganizationUserStatus = OrganizationUserStatusType.Revoked, + PolicyType = PolicyType.TwoFactorAuthentication + } + ])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new SingleOrganizationPolicyRequirement([])); - // User1 has 2FA, User2 doesn't + // User1 has 2FA sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Is>(ids => ids.Contains(orgUser1.UserId!.Value))) .Returns(new List<(Guid userId, bool twoFactorIsEnabled)> @@ -1172,8 +1020,20 @@ public async Task RestoreUsers_UserOwnsAnotherOrganizationButIsOnlyUserOfCurrent .GetManyByManyUsersAsync(Arg.Any>()) .Returns([orgUserFromOtherOrg]); - sutProvider.GetDependency().GetPoliciesApplicableToUserAsync(Arg.Any(), PolicyType.TwoFactorAuthentication, Arg.Any()) - .Returns([new OrganizationUserPolicyDetails { OrganizationId = organization.Id, PolicyType = PolicyType.TwoFactorAuthentication }]); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement( + [ + new PolicyDetails + { + OrganizationId = organization.Id, + OrganizationUserStatus = OrganizationUserStatusType.Revoked, + PolicyType = PolicyType.TwoFactorAuthentication + } + ])); + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new SingleOrganizationPolicyRequirement([])); // Act var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id], owner.Id, userService, null); @@ -1217,6 +1077,11 @@ private static void RestoreUser_Setup( sutProvider.GetDependency() .GetAsync(Arg.Any()) .Returns(new SingleOrganizationPolicyRequirement([])); + + // Setup default empty RequireTwoFactorPolicyRequirement for any user + sutProvider.GetDependency() + .GetAsync(Arg.Any()) + .Returns(new RequireTwoFactorPolicyRequirement([])); } private static void SetupOrganizationDataOwnershipPolicy( diff --git a/test/Core.Test/Services/UserServiceTests.cs b/test/Core.Test/Services/UserServiceTests.cs index 8c01f21d3893..1f388b66639f 100644 --- a/test/Core.Test/Services/UserServiceTests.cs +++ b/test/Core.Test/Services/UserServiceTests.cs @@ -7,7 +7,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Services; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; @@ -20,7 +19,6 @@ using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Data.Organizations; -using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; @@ -107,7 +105,6 @@ public async Task HasPremiumFromOrganization_Returns_False_If_Org_Not_Eligible(b var orgAbilities = new Dictionary() { { organization.Id, new OrganizationAbility(organization) } }; sutProvider.GetDependency().GetManyByUserAsync(user.Id).Returns(new List() { orgUser }); - sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(orgAbilities); Assert.False(await sutProvider.Sut.HasPremiumFromOrganization(user)); } @@ -120,7 +117,6 @@ public async Task HasPremiumFromOrganization_Returns_True_If_Org_Eligible(SutPro var orgAbilities = new Dictionary() { { organization.Id, new OrganizationAbility(organization) } }; sutProvider.GetDependency().GetManyByUserAsync(user.Id).Returns(new List() { orgUser }); - sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(orgAbilities); sutProvider.GetDependency().HasPremiumFromOrganizationAsync(user.Id).Returns(true); Assert.True(await sutProvider.Sut.HasPremiumFromOrganization(user)); @@ -226,81 +222,6 @@ public async Task DisableTwoFactorProviderAsync_WhenOrganizationHas2FAPolicyEnab SutProvider sutProvider, User user, Organization organization1, Guid organizationUserId1, Organization organization2, Guid organizationUserId2) - { - // Arrange - user.SetTwoFactorProviders(new Dictionary - { - [TwoFactorProviderType.Email] = new() { Enabled = true } - }); - organization1.Enabled = organization2.Enabled = true; - organization1.UseSso = organization2.UseSso = true; - - sutProvider.GetDependency() - .GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication) - .Returns( - [ - new OrganizationUserPolicyDetails - { - OrganizationId = organization1.Id, - OrganizationUserId = organizationUserId1, - PolicyType = PolicyType.TwoFactorAuthentication, - PolicyEnabled = true - }, - new OrganizationUserPolicyDetails - { - OrganizationId = organization2.Id, - OrganizationUserId = organizationUserId2, - PolicyType = PolicyType.TwoFactorAuthentication, - PolicyEnabled = true - } - ]); - sutProvider.GetDependency() - .GetByIdAsync(organization1.Id) - .Returns(organization1); - sutProvider.GetDependency() - .GetByIdAsync(organization2.Id) - .Returns(organization2); - 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); - - // Revoke the user from the first organization - await sutProvider.GetDependency() - .Received(1) - .RevokeNonCompliantOrganizationUsersAsync( - Arg.Is(r => r.OrganizationId == organization1.Id && - r.OrganizationUsers.First().Id == organizationUserId1 && - r.OrganizationUsers.First().OrganizationId == organization1.Id)); - await sutProvider.GetDependency() - .Received(1) - .SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(organization1.DisplayName(), user.Email); - - // Remove the user from the second organization - await sutProvider.GetDependency() - .Received(1) - .RevokeNonCompliantOrganizationUsersAsync( - Arg.Is(r => r.OrganizationId == organization2.Id && - r.OrganizationUsers.First().Id == organizationUserId2 && - r.OrganizationUsers.First().OrganizationId == organization2.Id)); - await sutProvider.GetDependency() - .Received(1) - .SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(organization2.DisplayName(), user.Email); - } - - [Theory, BitAutoData] - public async Task DisableTwoFactorProviderAsync_WithPolicyRequirementsEnabled_WhenOrganizationHas2FAPolicyEnabled_DisablingAllProviders_RevokesUserAndSendsEmail( - SutProvider sutProvider, User user, - Organization organization1, Guid organizationUserId1, - Organization organization2, Guid organizationUserId2) { user.SetTwoFactorProviders(new Dictionary { @@ -309,9 +230,6 @@ public async Task DisableTwoFactorProviderAsync_WithPolicyRequirementsEnabled_Wh organization1.Enabled = organization2.Enabled = true; organization1.UseSso = organization2.UseSso = true; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); sutProvider.GetDependency() .GetAsync(user.Id) .Returns(new RequireTwoFactorPolicyRequirement( @@ -371,62 +289,12 @@ await sutProvider.GetDependency() [Theory, BitAutoData] public async Task DisableTwoFactorProviderAsync_UserHasOneProviderEnabled_DoesNotRevokeUserFromOrganization( 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() - .DidNotReceiveWithAnyArgs() - .RevokeNonCompliantOrganizationUsersAsync(default); - await sutProvider.GetDependency() - .DidNotReceiveWithAnyArgs() - .SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(default, default); - } - - [Theory, BitAutoData] - public async Task DisableTwoFactorProviderAsync_WithPolicyRequirementsEnabled_UserHasOneProviderEnabled_DoesNotRevokeUserFromOrganization( - SutProvider sutProvider, User user, Organization organization) { user.SetTwoFactorProviders(new Dictionary { [TwoFactorProviderType.Email] = new() { Enabled = true }, [TwoFactorProviderType.Remember] = new() { Enabled = true } }); - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); sutProvider.GetDependency() .GetAsync(user.Id) .Returns(new RequireTwoFactorPolicyRequirement( @@ -554,6 +422,10 @@ public async Task RecoverTwoFactorAsync_CorrectCode_ReturnsTrueAndProcessesPolic var recoveryCode = "1234"; user.TwoFactorRecoveryCode = recoveryCode; + sutProvider.GetDependency() + .GetAsync(user.Id) + .Returns(new RequireTwoFactorPolicyRequirement([])); + // Act var response = await sutProvider.Sut.RecoverTwoFactorAsync(user, recoveryCode);