mirror of
https://github.com/bitwarden/server
synced 2025-12-17 16:53:23 +00:00
[PM-18238] Add RequireTwoFactorPolicyRequirement (#5840)
* Add RequireTwoFactorPolicyRequirement and its factory with unit tests * Implemented RequireTwoFactorPolicyRequirement to enforce two-factor authentication policies. * Created RequireTwoFactorPolicyRequirementFactory to generate policy requirements based on user status. * Added unit tests for the factory to validate behavior with various user statuses and policy details. * Enhance AcceptOrgUserCommand to use IPolicyRequirementQuery for two-factor authentication validation * Update ConfirmOrganizationUserCommand to use RequireTwoFactorPolicyRequirement to check for 2FA requirement * Implement CanAcceptInvitation and CanBeConfirmed methods in RequireTwoFactorPolicyRequirement; update tests to reflect new logic for two-factor authentication policy handling. * Refactor AcceptOrgUserCommand to enforce two-factor authentication policy based on feature flag; update validation logic and tests accordingly. * Enhance ConfirmOrganizationUserCommand to validate two-factor authentication policy based on feature flag; refactor validation logic and update related tests for improved policy handling. * Remove unused method and its dependencies from OrganizationService. * Implement CanBeRestored method in RequireTwoFactorPolicyRequirement to determine user restoration eligibility based on two-factor authentication status; add corresponding unit tests for various scenarios. * Update RestoreOrganizationUserCommand to use IPolicyRequirementQuery for two-factor authentication policies checks * Remove redundant vNext tests * Add TwoFactorPoliciesForActiveMemberships property to RequireTwoFactorPolicyRequirement and corresponding unit tests for policy retrieval based on user status * Refactor UserService to integrate IPolicyRequirementQuery for two-factor authentication policy checks * Add XML documentation for TwoFactorPoliciesForActiveMemberships property in RequireTwoFactorPolicyRequirement to clarify its purpose and return value. * Add exception documentation for ValidateTwoFactorAuthenticationPolicyAsync method in ConfirmOrganizationUserCommand to clarify error handling for users without two-step login enabled. * Update comments in AcceptOrgUserCommand and ConfirmOrganizationUserCommand to clarify handling of two-step login and 2FA policy checks. * Add RequireTwoFactorPolicyRequirementFactory to PolicyServiceCollectionExtensions * 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. * Add PolicyRequirements namespace * Update comments in AcceptOrgUserCommand and ConfirmOrganizationUserCommand to clarify two-factor authentication policy requirements and exception handling. * Refactor RequireTwoFactorPolicyRequirement to return tuples of (OrganizationId, OrganizationUserId) for active memberships requiring two-factor authentication. Update UserService and related tests to reflect this change. * Refactor AcceptOrgUserCommand: delegate feature flag check to the ValidateTwoFactorAuthenticationPolicyAsync method * Skip policy check if two-step login is enabled for the user * Refactor ConfirmOrganizationUserCommand to streamline two-factor authentication policy validation logic * Refactor AcceptOrgUserCommand to simplify two-factor authentication check by removing intermediate variable * Update documentation in RequireTwoFactorPolicyRequirement to clarify the purpose of the IsTwoFactorRequiredForOrganization * Refactor AcceptOrgUserCommandTests to remove redundant two-factor authentication checks and simplify test setup * Refactor AcceptOrgUserCommand and ConfirmOrganizationUserCommand to streamline two-factor authentication checks by removing redundant conditions and simplifying logic flow. * Rename removeOrgUserTasks variable in UserService * Refactor RestoreOrganizationUserCommand to simplify two-factor authentication compliance checks by consolidating logic into a new method, IsTwoFactorRequiredForOrganizationAsync. * Remove outdated two-factor authentication validation documentation from AcceptOrgUserCommand * Invert two-factor compliance check in RestoreOrganizationUserCommand to ensure correct validation of organization user policies. * Refactor UserService to enhance two-factor compliance checks by optimizing organization retrieval and logging when no organizations require two-factor authentication.
This commit is contained in:
@@ -6,6 +6,8 @@ using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
||||
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;
|
||||
@@ -81,6 +83,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
||||
private readonly IRevokeNonCompliantOrganizationUserCommand _revokeNonCompliantOrganizationUserCommand;
|
||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||
private readonly IDistributedCache _distributedCache;
|
||||
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
||||
|
||||
public UserService(
|
||||
IUserRepository userRepository,
|
||||
@@ -119,7 +122,8 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
||||
IRemoveOrganizationUserCommand removeOrganizationUserCommand,
|
||||
IRevokeNonCompliantOrganizationUserCommand revokeNonCompliantOrganizationUserCommand,
|
||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||
IDistributedCache distributedCache)
|
||||
IDistributedCache distributedCache,
|
||||
IPolicyRequirementQuery policyRequirementQuery)
|
||||
: base(
|
||||
store,
|
||||
optionsAccessor,
|
||||
@@ -164,6 +168,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
||||
_revokeNonCompliantOrganizationUserCommand = revokeNonCompliantOrganizationUserCommand;
|
||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||
_distributedCache = distributedCache;
|
||||
_policyRequirementQuery = policyRequirementQuery;
|
||||
}
|
||||
|
||||
public Guid? GetProperUserId(ClaimsPrincipal principal)
|
||||
@@ -1394,9 +1399,40 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
||||
|
||||
private async Task CheckPoliciesOnTwoFactorRemovalAsync(User user)
|
||||
{
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements))
|
||||
{
|
||||
var requirement = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(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);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var twoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication);
|
||||
|
||||
var removeOrgUserTasks = twoFactorPolicies.Select(async p =>
|
||||
var legacyRevokeOrgUserTasks = twoFactorPolicies.Select(async p =>
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(p.OrganizationId);
|
||||
await _revokeNonCompliantOrganizationUserCommand.RevokeNonCompliantOrganizationUsersAsync(
|
||||
@@ -1407,7 +1443,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
||||
await _mailService.SendOrganizationUserRevokedForTwoFactorPolicyEmailAsync(organization.DisplayName(), user.Email);
|
||||
}).ToArray();
|
||||
|
||||
await Task.WhenAll(removeOrgUserTasks);
|
||||
await Task.WhenAll(legacyRevokeOrgUserTasks);
|
||||
}
|
||||
|
||||
public override async Task<IdentityResult> ConfirmEmailAsync(User user, string token)
|
||||
|
||||
Reference in New Issue
Block a user