mirror of
https://github.com/bitwarden/server.git
synced 2025-06-05 02:30:32 -05:00
Refactor two-factor authentication policy checks in AcceptOrgUserCommand and ConfirmOrganizationUserCommand to streamline validation logic and improve clarity. Update RequireTwoFactorPolicyRequirement to provide a method for checking if two-factor authentication is required for an organization. Adjust related unit tests accordingly.
This commit is contained in:
parent
ee660b25b7
commit
7bcf1c1281
@ -246,17 +246,10 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
|||||||
/// <exception cref="BadRequestException">Thrown if the user does not have two-step login enabled.</exception>
|
/// <exception cref="BadRequestException">Thrown if the user does not have two-step login enabled.</exception>
|
||||||
private async Task ValidateTwoFactorAuthenticationPolicyAsync(User user, Guid organizationId)
|
private async Task ValidateTwoFactorAuthenticationPolicyAsync(User user, Guid organizationId)
|
||||||
{
|
{
|
||||||
var userTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
var twoFactorPolicyRequirement = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id);
|
||||||
if (userTwoFactorEnabled)
|
var twoFactorRequiredForOrganization = twoFactorPolicyRequirement.IsTwoFactorRequiredForOrganization(organizationId);
|
||||||
{
|
|
||||||
// If the user has two-step login enabled, we skip checking the 2FA policies
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var requirement = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id);
|
if (twoFactorRequiredForOrganization && !await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user))
|
||||||
var canAcceptInvitation = requirement.CanAcceptInvitation(userTwoFactorEnabled, organizationId);
|
|
||||||
|
|
||||||
if (!canAcceptInvitation)
|
|
||||||
{
|
{
|
||||||
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.");
|
||||||
}
|
}
|
||||||
|
@ -194,14 +194,14 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
|||||||
{
|
{
|
||||||
if (userTwoFactorEnabled)
|
if (userTwoFactorEnabled)
|
||||||
{
|
{
|
||||||
// If the user has two-step login enabled, we skip checking the 2FA policies
|
// If the user has two-step login enabled, we skip checking the 2FA policy
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var requirement = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id);
|
var twoFactorPolicyRequirement = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id);
|
||||||
var canBeConfirmed = requirement.CanBeConfirmed(userTwoFactorEnabled, organizationId);
|
var twoFactorRequired = twoFactorPolicyRequirement.IsTwoFactorRequiredForOrganization(organizationId);
|
||||||
|
|
||||||
if (!canBeConfirmed)
|
if (twoFactorRequired)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("User does not have two-step login enabled.");
|
throw new BadRequestException("User does not have two-step login enabled.");
|
||||||
}
|
}
|
||||||
|
@ -276,7 +276,7 @@ public class RestoreOrganizationUserCommand(
|
|||||||
if (featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements))
|
if (featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements))
|
||||||
{
|
{
|
||||||
var requirement = await policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(userId);
|
var requirement = await policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(userId);
|
||||||
twoFactorCompliant = requirement.CanBeRestored(userHasTwoFactorEnabled, orgUser.OrganizationId);
|
twoFactorCompliant = !requirement.IsTwoFactorRequiredForOrganization(orgUser.OrganizationId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -16,53 +16,20 @@ public class RequireTwoFactorPolicyRequirement : IPolicyRequirement
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines if the user can accept an invitation to an organization.
|
/// Checks if two-factor authentication is required for the organization due to an active policy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="twoFactorEnabled">Whether the user has two-step login enabled.</param>
|
/// <param name="organizationId">The ID of the organization to check.</param>
|
||||||
/// <param name="organizationId">The ID of the organization.</param>
|
/// <returns>True if two-factor authentication is required for the organization, false otherwise.</returns>
|
||||||
/// <returns>True if the user can accept the invitation, false otherwise.</returns>
|
/// <remarks>
|
||||||
public bool CanAcceptInvitation(bool twoFactorEnabled, Guid organizationId) =>
|
/// This does not check the user's membership status.
|
||||||
twoFactorEnabled ||
|
/// </remarks>
|
||||||
!_policyDetails.Any(p => p.OrganizationId == organizationId &&
|
public bool IsTwoFactorRequiredForOrganization(Guid organizationId) =>
|
||||||
(p.OrganizationUserStatus is
|
_policyDetails.Any(p => p.OrganizationId == organizationId);
|
||||||
OrganizationUserStatusType.Invited or
|
|
||||||
OrganizationUserStatusType.Accepted or
|
|
||||||
OrganizationUserStatusType.Confirmed));
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines if the user can be confirmed in an organization.
|
/// Gets the active two-factor authentication policies for active memberships.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="twoFactorEnabled">Whether the user has two-step login enabled.</param>
|
/// <returns>The active two-factor authentication policies for active memberships.</returns>
|
||||||
/// <param name="organizationId">The ID of the organization.</param>
|
|
||||||
/// <returns>True if the user can be confirmed, false otherwise.</returns>
|
|
||||||
public bool CanBeConfirmed(bool twoFactorEnabled, Guid organizationId) =>
|
|
||||||
twoFactorEnabled ||
|
|
||||||
!_policyDetails.Any(p => p.OrganizationId == organizationId &&
|
|
||||||
(p.OrganizationUserStatus is
|
|
||||||
OrganizationUserStatusType.Accepted or
|
|
||||||
OrganizationUserStatusType.Confirmed));
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines if the user can be restored in an organization.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="twoFactorEnabled">Whether the user has two-step login enabled.</param>
|
|
||||||
/// <param name="organizationId">The ID of the organization.</param>
|
|
||||||
/// <returns>True if the user can be restored, false otherwise.</returns>
|
|
||||||
public bool CanBeRestored(bool twoFactorEnabled, Guid organizationId) =>
|
|
||||||
twoFactorEnabled ||
|
|
||||||
!_policyDetails.Any(p => p.OrganizationId == organizationId &&
|
|
||||||
(p.OrganizationUserStatus is
|
|
||||||
OrganizationUserStatusType.Revoked or
|
|
||||||
OrganizationUserStatusType.Invited or
|
|
||||||
OrganizationUserStatusType.Accepted or
|
|
||||||
OrganizationUserStatusType.Confirmed));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the two-factor policies for active memberships.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The two-factor policies for active memberships.</returns>
|
|
||||||
public IEnumerable<PolicyDetails> TwoFactorPoliciesForActiveMemberships =>
|
public IEnumerable<PolicyDetails> TwoFactorPoliciesForActiveMemberships =>
|
||||||
_policyDetails.Where(p => p.OrganizationUserStatus is
|
_policyDetails.Where(p => p.OrganizationUserStatus is
|
||||||
OrganizationUserStatusType.Accepted or
|
OrganizationUserStatusType.Accepted or
|
||||||
|
@ -253,10 +253,6 @@ public class AcceptOrgUserCommandTests
|
|||||||
|
|
||||||
await sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService);
|
await sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRequirementQuery>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.GetAsync<RequireTwoFactorPolicyRequirement>(default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
.Received(1)
|
.Received(1)
|
||||||
.ReplaceAsync(Arg.Is<OrganizationUser>(ou => ou.Status == OrganizationUserStatusType.Accepted));
|
.ReplaceAsync(Arg.Is<OrganizationUser>(ou => ou.Status == OrganizationUserStatusType.Accepted));
|
||||||
|
@ -11,24 +11,20 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Policies.PolicyRequire
|
|||||||
public class RequireTwoFactorPolicyRequirementFactoryTests
|
public class RequireTwoFactorPolicyRequirementFactoryTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(true)]
|
[BitAutoData]
|
||||||
[BitAutoData(false)]
|
public void IsTwoFactorRequiredForOrganization_WithNoPolicies_ReturnsFalse(
|
||||||
public void CanAcceptInvitation_WithNoPolicies_ReturnsTrue(
|
Guid organizationId,
|
||||||
bool twoFactorEnabled, Guid organizationId,
|
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
||||||
{
|
{
|
||||||
var actual = sutProvider.Sut.Create([]);
|
var actual = sutProvider.Sut.Create([]);
|
||||||
|
|
||||||
Assert.True(actual.CanAcceptInvitation(twoFactorEnabled, organizationId));
|
Assert.False(actual.IsTwoFactorRequiredForOrganization(organizationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(OrganizationUserStatusType.Revoked)]
|
[BitAutoData]
|
||||||
[BitAutoData(OrganizationUserStatusType.Invited)]
|
public void IsTwoFactorRequiredForOrganization_WithOrganizationPolicy_ReturnsTrue(
|
||||||
[BitAutoData(OrganizationUserStatusType.Accepted)]
|
Guid organizationId,
|
||||||
[BitAutoData(OrganizationUserStatusType.Confirmed)]
|
|
||||||
public void CanAcceptInvitation_WithTwoFactorEnabled_ReturnsTrue(
|
|
||||||
OrganizationUserStatusType userStatus, Guid organizationId,
|
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
||||||
{
|
{
|
||||||
var actual = sutProvider.Sut.Create(
|
var actual = sutProvider.Sut.Create(
|
||||||
@ -37,179 +33,28 @@ public class RequireTwoFactorPolicyRequirementFactoryTests
|
|||||||
{
|
{
|
||||||
OrganizationId = organizationId,
|
OrganizationId = organizationId,
|
||||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||||
OrganizationUserStatus = userStatus
|
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Assert.True(actual.CanAcceptInvitation(true, organizationId));
|
Assert.True(actual.IsTwoFactorRequiredForOrganization(organizationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(OrganizationUserStatusType.Revoked)]
|
[BitAutoData]
|
||||||
public void CanAcceptInvitation_WithExemptStatus_ReturnsTrue(
|
public void IsTwoFactorRequiredForOrganization_WithOtherOrganizationPolicy_ReturnsFalse(
|
||||||
OrganizationUserStatusType userStatus, Guid organizationId,
|
Guid organizationId,
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
||||||
{
|
{
|
||||||
var actual = sutProvider.Sut.Create(
|
var actual = sutProvider.Sut.Create(
|
||||||
[
|
[
|
||||||
new PolicyDetails
|
new PolicyDetails
|
||||||
{
|
{
|
||||||
OrganizationId = organizationId,
|
OrganizationId = Guid.NewGuid(),
|
||||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||||
OrganizationUserStatus = userStatus
|
},
|
||||||
}
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Assert.True(actual.CanAcceptInvitation(false, organizationId));
|
Assert.False(actual.IsTwoFactorRequiredForOrganization(organizationId));
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Invited)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Accepted)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Confirmed)]
|
|
||||||
public void CanAcceptInvitation_WithNonExemptStatus_ReturnsFalse(
|
|
||||||
OrganizationUserStatusType userStatus, Guid organizationId,
|
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
|
||||||
{
|
|
||||||
var actual = sutProvider.Sut.Create(
|
|
||||||
[
|
|
||||||
new PolicyDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organizationId,
|
|
||||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
|
||||||
OrganizationUserStatus = userStatus
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
Assert.False(actual.CanAcceptInvitation(false, organizationId));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(true)]
|
|
||||||
[BitAutoData(false)]
|
|
||||||
public void CanBeConfirmed_WithNoPolicies_ReturnsTrue(
|
|
||||||
bool twoFactorEnabled, Guid organizationId,
|
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
|
||||||
{
|
|
||||||
var actual = sutProvider.Sut.Create([]);
|
|
||||||
|
|
||||||
Assert.True(actual.CanBeConfirmed(twoFactorEnabled, organizationId));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Accepted)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Confirmed)]
|
|
||||||
public void CanBeConfirmed_WithTwoFactorEnabled_ReturnsTrue(
|
|
||||||
OrganizationUserStatusType userStatus, Guid organizationId,
|
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
|
||||||
{
|
|
||||||
var actual = sutProvider.Sut.Create(
|
|
||||||
[
|
|
||||||
new PolicyDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organizationId,
|
|
||||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
|
||||||
OrganizationUserStatus = userStatus
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
Assert.True(actual.CanBeConfirmed(true, organizationId));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Revoked)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Invited)]
|
|
||||||
public void CanBeConfirmed_WithExemptStatus_ReturnsTrue(
|
|
||||||
OrganizationUserStatusType userStatus, Guid organizationId,
|
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
|
||||||
{
|
|
||||||
var actual = sutProvider.Sut.Create(
|
|
||||||
[
|
|
||||||
new PolicyDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organizationId,
|
|
||||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
|
||||||
OrganizationUserStatus = userStatus
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
Assert.True(actual.CanBeConfirmed(false, organizationId));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Accepted)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Confirmed)]
|
|
||||||
public void CanBeConfirmed_WithNonExemptStatus_ReturnsFalse(
|
|
||||||
OrganizationUserStatusType userStatus, Guid organizationId,
|
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
|
||||||
{
|
|
||||||
var actual = sutProvider.Sut.Create(
|
|
||||||
[
|
|
||||||
new PolicyDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organizationId,
|
|
||||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
|
||||||
OrganizationUserStatus = userStatus
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
Assert.False(actual.CanBeConfirmed(false, organizationId));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(true)]
|
|
||||||
[BitAutoData(false)]
|
|
||||||
public void CanBeRestored_WithNoPolicies_ReturnsTrue(
|
|
||||||
bool twoFactorEnabled, Guid organizationId,
|
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
|
||||||
{
|
|
||||||
var actual = sutProvider.Sut.Create([]);
|
|
||||||
|
|
||||||
Assert.True(actual.CanBeRestored(twoFactorEnabled, organizationId));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Revoked)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Invited)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Accepted)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Confirmed)]
|
|
||||||
public void CanBeRestored_WithTwoFactorEnabled_ReturnsTrue(
|
|
||||||
OrganizationUserStatusType userStatus, Guid organizationId,
|
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
|
||||||
{
|
|
||||||
var actual = sutProvider.Sut.Create(
|
|
||||||
[
|
|
||||||
new PolicyDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organizationId,
|
|
||||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
|
||||||
OrganizationUserStatus = userStatus
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
Assert.True(actual.CanBeRestored(true, organizationId));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Revoked)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Invited)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Accepted)]
|
|
||||||
[BitAutoData(OrganizationUserStatusType.Confirmed)]
|
|
||||||
public void CanBeRestored_WithAnyStatus_ReturnsFalse(
|
|
||||||
OrganizationUserStatusType userStatus, Guid organizationId,
|
|
||||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
|
||||||
{
|
|
||||||
var actual = sutProvider.Sut.Create(
|
|
||||||
[
|
|
||||||
new PolicyDetails
|
|
||||||
{
|
|
||||||
OrganizationId = organizationId,
|
|
||||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
|
||||||
OrganizationUserStatus = userStatus
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
Assert.False(actual.CanBeRestored(false, organizationId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user