From f4bfa0baf035599ef89d1ce462d4ea49fab1f302 Mon Sep 17 00:00:00 2001 From: Rui Tome Date: Tue, 20 May 2025 16:47:52 +0100 Subject: [PATCH] Implement CanAcceptInvitation and CanBeConfirmed methods in RequireTwoFactorPolicyRequirement; update tests to reflect new logic for two-factor authentication policy handling. --- .../RequireTwoFactorPolicyRequirement.cs | 39 +++++- ...eTwoFactorPolicyRequirementFactoryTests.cs | 117 ++++++++++++++++-- 2 files changed, 141 insertions(+), 15 deletions(-) diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyRequirements/RequireTwoFactorPolicyRequirement.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyRequirements/RequireTwoFactorPolicyRequirement.cs index 2ce81ed9d7..08fbb72429 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyRequirements/RequireTwoFactorPolicyRequirement.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyRequirements/RequireTwoFactorPolicyRequirement.cs @@ -8,10 +8,40 @@ using Bit.Core.Enums; /// public class RequireTwoFactorPolicyRequirement : IPolicyRequirement { + private readonly IEnumerable _policyDetails; + + public RequireTwoFactorPolicyRequirement(IEnumerable policyDetails) + { + _policyDetails = policyDetails; + } + /// - /// Indicates whether two-factor authentication is required for the user. + /// Determines if the user can accept an invitation to an organization. /// - public bool RequireTwoFactor { get; init; } + /// Whether the user has two-step login enabled. + /// The ID of the organization. + /// True if the user can accept the invitation, false otherwise. + public bool CanAcceptInvitation(bool twoFactorEnabled, Guid organizationId) => + twoFactorEnabled || + !_policyDetails.Any(p => p.OrganizationId == organizationId && + (p.OrganizationUserStatus is + OrganizationUserStatusType.Invited or + OrganizationUserStatusType.Accepted or + OrganizationUserStatusType.Confirmed)); + + + /// + /// Determines if the user can be confirmed in an organization. + /// + /// Whether the user has two-step login enabled. + /// The ID of the organization. + /// True if the user can be confirmed, false otherwise. + public bool CanBeConfirmed(bool twoFactorEnabled, Guid organizationId) => + twoFactorEnabled || + !_policyDetails.Any(p => p.OrganizationId == organizationId && + (p.OrganizationUserStatus is + OrganizationUserStatusType.Accepted or + OrganizationUserStatusType.Confirmed)); } public class RequireTwoFactorPolicyRequirementFactory : BasePolicyRequirementFactory @@ -21,9 +51,6 @@ public class RequireTwoFactorPolicyRequirementFactory : BasePolicyRequirementFac public override RequireTwoFactorPolicyRequirement Create(IEnumerable policyDetails) { - return new RequireTwoFactorPolicyRequirement - { - RequireTwoFactor = policyDetails.Any(p => p.PolicyType == PolicyType.TwoFactorAuthentication) - }; + return new RequireTwoFactorPolicyRequirement(policyDetails); } } diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyRequirements/RequireTwoFactorPolicyRequirementFactoryTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyRequirements/RequireTwoFactorPolicyRequirementFactoryTests.cs index 2b65ad5157..dd8922ba15 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyRequirements/RequireTwoFactorPolicyRequirementFactoryTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/Policies/PolicyRequirements/RequireTwoFactorPolicyRequirementFactoryTests.cs @@ -11,49 +11,148 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Policies.PolicyRequire public class RequireTwoFactorPolicyRequirementFactoryTests { [Theory] - [BitAutoData] - public void RequireTwoFactor_WithNoPolicies_ReturnsFalse(SutProvider sutProvider) + [BitAutoData(true)] + [BitAutoData(false)] + public void CanAcceptInvitation_WithNoPolicies_ReturnsTrue( + bool twoFactorEnabled, Guid organizationId, + SutProvider sutProvider) { var actual = sutProvider.Sut.Create([]); - Assert.False(actual.RequireTwoFactor); + Assert.True(actual.CanAcceptInvitation(twoFactorEnabled, organizationId)); + } + + [Theory] + [BitAutoData(OrganizationUserStatusType.Revoked)] + [BitAutoData(OrganizationUserStatusType.Invited)] + [BitAutoData(OrganizationUserStatusType.Accepted)] + [BitAutoData(OrganizationUserStatusType.Confirmed)] + public void CanAcceptInvitation_WithTwoFactorEnabled_ReturnsTrue( + OrganizationUserStatusType userStatus, Guid organizationId, + SutProvider sutProvider) + { + var actual = sutProvider.Sut.Create( + [ + new PolicyDetails + { + OrganizationId = organizationId, + PolicyType = PolicyType.TwoFactorAuthentication, + OrganizationUserStatus = userStatus + } + ]); + + Assert.True(actual.CanAcceptInvitation(true, organizationId)); + } + + [Theory] + [BitAutoData(OrganizationUserStatusType.Revoked)] + public void CanAcceptInvitation_WithExemptStatus_ReturnsTrue( + OrganizationUserStatusType userStatus, Guid organizationId, + SutProvider sutProvider) + { + var actual = sutProvider.Sut.Create( + [ + new PolicyDetails + { + OrganizationId = organizationId, + PolicyType = PolicyType.TwoFactorAuthentication, + OrganizationUserStatus = userStatus + } + ]); + + Assert.True(actual.CanAcceptInvitation(false, organizationId)); } [Theory] [BitAutoData(OrganizationUserStatusType.Invited)] [BitAutoData(OrganizationUserStatusType.Accepted)] [BitAutoData(OrganizationUserStatusType.Confirmed)] - public void RequireTwoFactor_WithNonExemptStatus_ReturnsTrue( - OrganizationUserStatusType userStatus, + public void CanAcceptInvitation_WithNonExemptStatus_ReturnsFalse( + OrganizationUserStatusType userStatus, Guid organizationId, SutProvider sutProvider) { var actual = sutProvider.Sut.Create( [ new PolicyDetails { + OrganizationId = organizationId, PolicyType = PolicyType.TwoFactorAuthentication, OrganizationUserStatus = userStatus } ]); - Assert.True(actual.RequireTwoFactor); + Assert.False(actual.CanAcceptInvitation(false, organizationId)); + } + + [Theory] + [BitAutoData(true)] + [BitAutoData(false)] + public void CanBeConfirmed_WithNoPolicies_ReturnsTrue( + bool twoFactorEnabled, Guid organizationId, + SutProvider 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 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)] - public void RequireTwoFactor_WithExemptStatus_ReturnsFalse( - OrganizationUserStatusType userStatus, + [BitAutoData(OrganizationUserStatusType.Invited)] + public void CanBeConfirmed_WithExemptStatus_ReturnsTrue( + OrganizationUserStatusType userStatus, Guid organizationId, SutProvider sutProvider) { var actual = sutProvider.Sut.Create( [ new PolicyDetails { + OrganizationId = organizationId, PolicyType = PolicyType.TwoFactorAuthentication, OrganizationUserStatus = userStatus } ]); - Assert.False(actual.RequireTwoFactor); + Assert.True(actual.CanBeConfirmed(false, organizationId)); + } + + [Theory] + [BitAutoData(OrganizationUserStatusType.Accepted)] + [BitAutoData(OrganizationUserStatusType.Confirmed)] + public void CanBeConfirmed_WithNonExemptStatus_ReturnsFalse( + OrganizationUserStatusType userStatus, Guid organizationId, + SutProvider sutProvider) + { + var actual = sutProvider.Sut.Create( + [ + new PolicyDetails + { + OrganizationId = organizationId, + PolicyType = PolicyType.TwoFactorAuthentication, + OrganizationUserStatus = userStatus + } + ]); + + Assert.False(actual.CanBeConfirmed(false, organizationId)); } }