mirror of
https://github.com/bitwarden/server.git
synced 2025-05-24 04:51:03 -05:00
Enhance AcceptOrgUserCommand to use IPolicyRequirementQuery for two-factor authentication validation
This commit is contained in:
parent
d696bfdc30
commit
e8a8d5c8c7
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Enums;
|
using Bit.Core.AdminConsole.Enums;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
@ -25,6 +26,8 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
|||||||
private readonly IMailService _mailService;
|
private readonly IMailService _mailService;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
||||||
|
private readonly IFeatureService _featureService;
|
||||||
|
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
||||||
|
|
||||||
public AcceptOrgUserCommand(
|
public AcceptOrgUserCommand(
|
||||||
IDataProtectionProvider dataProtectionProvider,
|
IDataProtectionProvider dataProtectionProvider,
|
||||||
@ -34,9 +37,10 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
|||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
IMailService mailService,
|
IMailService mailService,
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory)
|
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
||||||
|
IFeatureService featureService,
|
||||||
|
IPolicyRequirementQuery policyRequirementQuery)
|
||||||
{
|
{
|
||||||
|
|
||||||
// TODO: remove data protector when old token validation removed
|
// TODO: remove data protector when old token validation removed
|
||||||
_dataProtector = dataProtectionProvider.CreateProtector(OrgUserInviteTokenable.DataProtectorPurpose);
|
_dataProtector = dataProtectionProvider.CreateProtector(OrgUserInviteTokenable.DataProtectorPurpose);
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
@ -46,6 +50,8 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
|||||||
_mailService = mailService;
|
_mailService = mailService;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
||||||
|
_featureService = featureService;
|
||||||
|
_policyRequirementQuery = policyRequirementQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OrganizationUser> AcceptOrgUserByEmailTokenAsync(Guid organizationUserId, User user, string emailToken,
|
public async Task<OrganizationUser> AcceptOrgUserByEmailTokenAsync(Guid organizationUserId, User user, string emailToken,
|
||||||
@ -194,12 +200,7 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
|||||||
// Enforce Two Factor Authentication Policy of organization user is trying to join
|
// Enforce Two Factor Authentication Policy of organization user is trying to join
|
||||||
if (!await userService.TwoFactorIsEnabledAsync(user))
|
if (!await userService.TwoFactorIsEnabledAsync(user))
|
||||||
{
|
{
|
||||||
var invitedTwoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id,
|
await ValidateTwoFactorAuthenticationPolicy(user, orgUser.OrganizationId);
|
||||||
PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited);
|
|
||||||
if (invitedTwoFactorPolicies.Any(p => p.OrganizationId == orgUser.OrganizationId))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("You cannot join this organization until you enable two-step login on your user account.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
orgUser.Status = OrganizationUserStatusType.Accepted;
|
orgUser.Status = OrganizationUserStatusType.Accepted;
|
||||||
@ -220,4 +221,24 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
|||||||
return orgUser;
|
return orgUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ValidateTwoFactorAuthenticationPolicy(User user, Guid organizationId)
|
||||||
|
{
|
||||||
|
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements))
|
||||||
|
{
|
||||||
|
var requireTwoFactorPolicyRequirement = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id);
|
||||||
|
if (requireTwoFactorPolicyRequirement.RequireTwoFactor)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("You cannot join this organization until you enable two-step login on your user account.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Enums;
|
using Bit.Core.AdminConsole.Enums;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
@ -183,6 +184,96 @@ public class AcceptOrgUserCommandTests
|
|||||||
exception.Message);
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task AcceptOrgUserAsync_WithPolicyRequirementsEnabled_UserWithout2FAJoining2FARequiredOrg_ThrowsBadRequest(
|
||||||
|
SutProvider<AcceptOrgUserCommand> sutProvider,
|
||||||
|
User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.PolicyRequirements)
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
// User doesn't have 2FA enabled
|
||||||
|
_userService.TwoFactorIsEnabledAsync(user).Returns(false);
|
||||||
|
|
||||||
|
// Organization they are trying to join requires 2FA
|
||||||
|
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||||
|
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||||
|
.Returns(new RequireTwoFactorPolicyRequirement { RequireTwoFactor = true });
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||||
|
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_UserWith2FAJoining2FARequiredOrg_Succeeds(
|
||||||
|
SutProvider<AcceptOrgUserCommand> sutProvider,
|
||||||
|
User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails)
|
||||||
|
{
|
||||||
|
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.PolicyRequirements)
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
// User has 2FA enabled
|
||||||
|
_userService.TwoFactorIsEnabledAsync(user).Returns(true);
|
||||||
|
|
||||||
|
// Organization they are trying to join requires 2FA
|
||||||
|
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||||
|
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||||
|
.Returns(new RequireTwoFactorPolicyRequirement { RequireTwoFactor = true });
|
||||||
|
|
||||||
|
await sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||||
|
.DidNotReceiveWithAnyArgs()
|
||||||
|
.GetAsync<RequireTwoFactorPolicyRequirement>(default);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.Received(1)
|
||||||
|
.ReplaceAsync(Arg.Is<OrganizationUser>(ou => ou.Status == OrganizationUserStatusType.Accepted));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(true)]
|
||||||
|
[BitAutoData(false)]
|
||||||
|
public async Task AcceptOrgUserAsync_WithPolicyRequirementsEnabled_UserJoiningOrgWithout2FARequirement_Succeeds(
|
||||||
|
bool userTwoFactorEnabled, SutProvider<AcceptOrgUserCommand> sutProvider,
|
||||||
|
User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.PolicyRequirements)
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
// User doesn't have 2FA enabled
|
||||||
|
_userService.TwoFactorIsEnabledAsync(user).Returns(userTwoFactorEnabled);
|
||||||
|
|
||||||
|
// Organization they are trying to join doesn't require 2FA
|
||||||
|
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||||
|
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||||
|
.Returns(new RequireTwoFactorPolicyRequirement { RequireTwoFactor = false });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
||||||
|
.ReplaceAsync(Arg.Is<OrganizationUser>(ou => ou.Status == OrganizationUserStatusType.Accepted));
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(OrganizationUserType.Admin)]
|
[BitAutoData(OrganizationUserType.Admin)]
|
||||||
[BitAutoData(OrganizationUserType.Owner)]
|
[BitAutoData(OrganizationUserType.Owner)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user