|
|
|
|
@@ -4,6 +4,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
|
|
|
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
|
|
|
|
|
using Bit.Core.AdminConsole.Repositories;
|
|
|
|
|
using Bit.Core.Enums;
|
|
|
|
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
|
|
|
|
using Bit.Core.Repositories;
|
|
|
|
|
|
|
|
|
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
|
|
|
|
@@ -17,26 +18,13 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
|
|
|
|
/// <li>All organization users are compliant with the Single organization policy</li>
|
|
|
|
|
/// <li>No provider users exist</li>
|
|
|
|
|
/// </ul>
|
|
|
|
|
///
|
|
|
|
|
/// This class also performs side effects when the policy is being enabled or disabled. They are:
|
|
|
|
|
/// <ul>
|
|
|
|
|
/// <li>Sets the UseAutomaticUserConfirmation organization feature to match the policy update</li>
|
|
|
|
|
/// </ul>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class AutomaticUserConfirmationPolicyEventHandler(
|
|
|
|
|
IOrganizationUserRepository organizationUserRepository,
|
|
|
|
|
IProviderUserRepository providerUserRepository,
|
|
|
|
|
IPolicyRepository policyRepository,
|
|
|
|
|
IOrganizationRepository organizationRepository,
|
|
|
|
|
TimeProvider timeProvider)
|
|
|
|
|
: IPolicyValidator, IPolicyValidationEvent, IOnPolicyPreUpdateEvent, IEnforceDependentPoliciesEvent
|
|
|
|
|
IProviderUserRepository providerUserRepository)
|
|
|
|
|
: IPolicyValidator, IPolicyValidationEvent, IEnforceDependentPoliciesEvent
|
|
|
|
|
{
|
|
|
|
|
public PolicyType Type => PolicyType.AutomaticUserConfirmation;
|
|
|
|
|
public async Task ExecutePreUpsertSideEffectAsync(SavePolicyModel policyRequest, Policy? currentPolicy) =>
|
|
|
|
|
await OnSaveSideEffectsAsync(policyRequest.PolicyUpdate, currentPolicy);
|
|
|
|
|
|
|
|
|
|
private const string _singleOrgPolicyNotEnabledErrorMessage =
|
|
|
|
|
"The Single organization policy must be enabled before enabling the Automatically confirm invited users policy.";
|
|
|
|
|
|
|
|
|
|
private const string _usersNotCompliantWithSingleOrgErrorMessage =
|
|
|
|
|
"All organization users must be compliant with the Single organization policy before enabling the Automatically confirm invited users policy. Please remove users who are members of multiple organizations.";
|
|
|
|
|
@@ -61,27 +49,20 @@ public class AutomaticUserConfirmationPolicyEventHandler(
|
|
|
|
|
public async Task<string> ValidateAsync(SavePolicyModel savePolicyModel, Policy? currentPolicy) =>
|
|
|
|
|
await ValidateAsync(savePolicyModel.PolicyUpdate, currentPolicy);
|
|
|
|
|
|
|
|
|
|
public async Task OnSaveSideEffectsAsync(PolicyUpdate policyUpdate, Policy? currentPolicy)
|
|
|
|
|
{
|
|
|
|
|
var organization = await organizationRepository.GetByIdAsync(policyUpdate.OrganizationId);
|
|
|
|
|
|
|
|
|
|
if (organization is not null)
|
|
|
|
|
{
|
|
|
|
|
organization.UseAutomaticUserConfirmation = policyUpdate.Enabled;
|
|
|
|
|
organization.RevisionDate = timeProvider.GetUtcNow().UtcDateTime;
|
|
|
|
|
await organizationRepository.UpsertAsync(organization);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public Task OnSaveSideEffectsAsync(PolicyUpdate policyUpdate, Policy? currentPolicy) =>
|
|
|
|
|
Task.CompletedTask;
|
|
|
|
|
|
|
|
|
|
private async Task<string> ValidateEnablingPolicyAsync(Guid organizationId)
|
|
|
|
|
{
|
|
|
|
|
var singleOrgValidationError = await ValidateSingleOrgPolicyComplianceAsync(organizationId);
|
|
|
|
|
var organizationUsers = await organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
|
|
|
|
|
|
|
|
|
|
var singleOrgValidationError = await ValidateUserComplianceWithSingleOrgAsync(organizationId, organizationUsers);
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(singleOrgValidationError))
|
|
|
|
|
{
|
|
|
|
|
return singleOrgValidationError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var providerValidationError = await ValidateNoProviderUsersAsync(organizationId);
|
|
|
|
|
var providerValidationError = await ValidateNoProviderUsersAsync(organizationUsers);
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(providerValidationError))
|
|
|
|
|
{
|
|
|
|
|
return providerValidationError;
|
|
|
|
|
@@ -90,42 +71,24 @@ public class AutomaticUserConfirmationPolicyEventHandler(
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<string> ValidateSingleOrgPolicyComplianceAsync(Guid organizationId)
|
|
|
|
|
private async Task<string> ValidateUserComplianceWithSingleOrgAsync(Guid organizationId,
|
|
|
|
|
ICollection<OrganizationUserUserDetails> organizationUsers)
|
|
|
|
|
{
|
|
|
|
|
var singleOrgPolicy = await policyRepository.GetByOrganizationIdTypeAsync(organizationId, PolicyType.SingleOrg);
|
|
|
|
|
if (singleOrgPolicy is not { Enabled: true })
|
|
|
|
|
{
|
|
|
|
|
return _singleOrgPolicyNotEnabledErrorMessage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await ValidateUserComplianceWithSingleOrgAsync(organizationId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<string> ValidateUserComplianceWithSingleOrgAsync(Guid organizationId)
|
|
|
|
|
{
|
|
|
|
|
var organizationUsers = (await organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId))
|
|
|
|
|
.Where(ou => ou.Status != OrganizationUserStatusType.Invited &&
|
|
|
|
|
ou.Status != OrganizationUserStatusType.Revoked &&
|
|
|
|
|
ou.UserId.HasValue)
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
if (organizationUsers.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var hasNonCompliantUser = (await organizationUserRepository.GetManyByManyUsersAsync(
|
|
|
|
|
organizationUsers.Select(ou => ou.UserId!.Value)))
|
|
|
|
|
.Any(uo => uo.OrganizationId != organizationId &&
|
|
|
|
|
uo.Status != OrganizationUserStatusType.Invited);
|
|
|
|
|
.Any(uo => uo.OrganizationId != organizationId
|
|
|
|
|
&& uo.Status != OrganizationUserStatusType.Invited);
|
|
|
|
|
|
|
|
|
|
return hasNonCompliantUser ? _usersNotCompliantWithSingleOrgErrorMessage : string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<string> ValidateNoProviderUsersAsync(Guid organizationId)
|
|
|
|
|
private async Task<string> ValidateNoProviderUsersAsync(ICollection<OrganizationUserUserDetails> organizationUsers)
|
|
|
|
|
{
|
|
|
|
|
var providerUsers = await providerUserRepository.GetManyByOrganizationAsync(organizationId);
|
|
|
|
|
var userIds = organizationUsers.Where(x => x.UserId is not null)
|
|
|
|
|
.Select(x => x.UserId!.Value);
|
|
|
|
|
|
|
|
|
|
return providerUsers.Count > 0 ? _providerUsersExistErrorMessage : string.Empty;
|
|
|
|
|
return (await providerUserRepository.GetManyByManyUsersAsync(userIds)).Count != 0
|
|
|
|
|
? _providerUsersExistErrorMessage
|
|
|
|
|
: string.Empty;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|