mirror of
https://github.com/bitwarden/server
synced 2026-01-04 17:43:53 +00:00
[PM-24278] Create Remove Individual Vault validator (#6139)
This commit is contained in:
@@ -278,6 +278,6 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
return;
|
||||
}
|
||||
|
||||
await _collectionRepository.CreateDefaultCollectionsAsync(organizationId, eligibleOrganizationUserIds, defaultUserCollectionName);
|
||||
await _collectionRepository.UpsertDefaultCollectionsAsync(organizationId, eligibleOrganizationUserIds, defaultUserCollectionName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
|
||||
@@ -24,19 +25,19 @@ public enum OrganizationDataOwnershipState
|
||||
/// </summary>
|
||||
public class OrganizationDataOwnershipPolicyRequirement : IPolicyRequirement
|
||||
{
|
||||
private readonly IEnumerable<Guid> _organizationIdsWithPolicyEnabled;
|
||||
private readonly IEnumerable<PolicyDetails> _policyDetails;
|
||||
|
||||
/// <param name="organizationDataOwnershipState">
|
||||
/// The organization data ownership state for the user.
|
||||
/// </param>
|
||||
/// <param name="organizationIdsWithPolicyEnabled">
|
||||
/// The collection of Organization IDs that have the Organization Data Ownership policy enabled.
|
||||
/// <param name="policyDetails">
|
||||
/// An enumerable collection of PolicyDetails for the organizations.
|
||||
/// </param>
|
||||
public OrganizationDataOwnershipPolicyRequirement(
|
||||
OrganizationDataOwnershipState organizationDataOwnershipState,
|
||||
IEnumerable<Guid> organizationIdsWithPolicyEnabled)
|
||||
IEnumerable<PolicyDetails> policyDetails)
|
||||
{
|
||||
_organizationIdsWithPolicyEnabled = organizationIdsWithPolicyEnabled ?? [];
|
||||
_policyDetails = policyDetails;
|
||||
State = organizationDataOwnershipState;
|
||||
}
|
||||
|
||||
@@ -46,14 +47,34 @@ public class OrganizationDataOwnershipPolicyRequirement : IPolicyRequirement
|
||||
public OrganizationDataOwnershipState State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the Organization Data Ownership policy is enforced in that organization.
|
||||
/// Gets a default collection request for enforcing the Organization Data Ownership policy.
|
||||
/// Only confirmed users are applicable.
|
||||
/// This indicates whether the user should have a default collection created for them when the policy is enabled,
|
||||
/// and if so, the relevant OrganizationUserId to create the collection for.
|
||||
/// </summary>
|
||||
public bool RequiresDefaultCollection(Guid organizationId)
|
||||
/// <param name="organizationId">The organization ID to create the request for.</param>
|
||||
/// <returns>A DefaultCollectionRequest containing the OrganizationUserId and a flag indicating whether to create a default collection.</returns>
|
||||
public DefaultCollectionRequest GetDefaultCollectionRequestOnPolicyEnable(Guid organizationId)
|
||||
{
|
||||
return _organizationIdsWithPolicyEnabled.Contains(organizationId);
|
||||
var policyDetail = _policyDetails
|
||||
.FirstOrDefault(p => p.OrganizationId == organizationId);
|
||||
|
||||
if (policyDetail != null && policyDetail.HasStatus([OrganizationUserStatusType.Confirmed]))
|
||||
{
|
||||
return new DefaultCollectionRequest(policyDetail.OrganizationUserId, true);
|
||||
}
|
||||
|
||||
var noCollectionNeeded = new DefaultCollectionRequest(Guid.Empty, false);
|
||||
return noCollectionNeeded;
|
||||
}
|
||||
}
|
||||
|
||||
public record DefaultCollectionRequest(Guid OrganizationUserId, bool ShouldCreateDefaultCollection)
|
||||
{
|
||||
public readonly bool ShouldCreateDefaultCollection = ShouldCreateDefaultCollection;
|
||||
public readonly Guid OrganizationUserId = OrganizationUserId;
|
||||
}
|
||||
|
||||
public class OrganizationDataOwnershipPolicyRequirementFactory : BasePolicyRequirementFactory<OrganizationDataOwnershipPolicyRequirement>
|
||||
{
|
||||
public override PolicyType PolicyType => PolicyType.OrganizationDataOwnership;
|
||||
@@ -63,10 +84,9 @@ public class OrganizationDataOwnershipPolicyRequirementFactory : BasePolicyRequi
|
||||
var organizationDataOwnershipState = policyDetails.Any()
|
||||
? OrganizationDataOwnershipState.Enabled
|
||||
: OrganizationDataOwnershipState.Disabled;
|
||||
var organizationIdsWithPolicyEnabled = policyDetails.Select(p => p.OrganizationId).ToHashSet();
|
||||
|
||||
return new OrganizationDataOwnershipPolicyRequirement(
|
||||
organizationDataOwnershipState,
|
||||
organizationIdsWithPolicyEnabled);
|
||||
policyDetails);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ public static class PolicyServiceCollectionExtensions
|
||||
services.AddScoped<IPolicyValidator, ResetPasswordPolicyValidator>();
|
||||
services.AddScoped<IPolicyValidator, MaximumVaultTimeoutPolicyValidator>();
|
||||
services.AddScoped<IPolicyValidator, FreeFamiliesForEnterprisePolicyValidator>();
|
||||
// This validator will be hooked up in https://bitwarden.atlassian.net/browse/PM-24279.
|
||||
// services.AddScoped<IPolicyValidator, OrganizationDataOwnershipPolicyValidator>();
|
||||
}
|
||||
|
||||
private static void AddPolicyRequirements(this IServiceCollection services)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#nullable enable
|
||||
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
||||
|
||||
public class OrganizationDataOwnershipPolicyValidator(
|
||||
IPolicyRepository policyRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IEnumerable<IPolicyRequirementFactory<IPolicyRequirement>> factories,
|
||||
IFeatureService featureService,
|
||||
ILogger<OrganizationDataOwnershipPolicyValidator> logger)
|
||||
: OrganizationPolicyValidator(policyRepository, factories)
|
||||
{
|
||||
public override PolicyType Type => PolicyType.OrganizationDataOwnership;
|
||||
|
||||
public override IEnumerable<PolicyType> RequiredPolicies => [];
|
||||
|
||||
public override Task<string> ValidateAsync(PolicyUpdate policyUpdate, Policy? currentPolicy) => Task.FromResult("");
|
||||
|
||||
public override async Task OnSaveSideEffectsAsync(PolicyUpdate policyUpdate, Policy? currentPolicy)
|
||||
{
|
||||
if (!featureService.IsEnabled(FeatureFlagKeys.CreateDefaultLocation))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentPolicy?.Enabled != true && policyUpdate.Enabled)
|
||||
{
|
||||
await UpsertDefaultCollectionsForUsersAsync(policyUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpsertDefaultCollectionsForUsersAsync(PolicyUpdate policyUpdate)
|
||||
{
|
||||
var requirements = await GetUserPolicyRequirementsByOrganizationIdAsync<OrganizationDataOwnershipPolicyRequirement>(policyUpdate.OrganizationId, policyUpdate.Type);
|
||||
|
||||
var userOrgIds = requirements
|
||||
.Select(requirement => requirement.GetDefaultCollectionRequestOnPolicyEnable(policyUpdate.OrganizationId))
|
||||
.Where(request => request.ShouldCreateDefaultCollection)
|
||||
.Select(request => request.OrganizationUserId);
|
||||
|
||||
if (!userOrgIds.Any())
|
||||
{
|
||||
logger.LogError("No UserOrganizationIds found for {OrganizationId}", policyUpdate.OrganizationId);
|
||||
return;
|
||||
}
|
||||
|
||||
await collectionRepository.UpsertDefaultCollectionsAsync(
|
||||
policyUpdate.OrganizationId,
|
||||
userOrgIds,
|
||||
GetDefaultUserCollectionName());
|
||||
}
|
||||
|
||||
private static string GetDefaultUserCollectionName()
|
||||
{
|
||||
// TODO: https://bitwarden.atlassian.net/browse/PM-24279
|
||||
const string temporaryPlaceHolderValue = "Default";
|
||||
return temporaryPlaceHolderValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
||||
|
||||
public abstract class OrganizationPolicyValidator(IPolicyRepository policyRepository, IEnumerable<IPolicyRequirementFactory<IPolicyRequirement>> factories) : IPolicyValidator
|
||||
{
|
||||
public abstract PolicyType Type { get; }
|
||||
|
||||
public abstract IEnumerable<PolicyType> RequiredPolicies { get; }
|
||||
|
||||
protected async Task<IEnumerable<T>> GetUserPolicyRequirementsByOrganizationIdAsync<T>(Guid organizationId, PolicyType policyType) where T : IPolicyRequirement
|
||||
{
|
||||
var factory = factories.OfType<IPolicyRequirementFactory<T>>().SingleOrDefault();
|
||||
if (factory is null)
|
||||
{
|
||||
throw new NotImplementedException("No Requirement Factory found for " + typeof(T));
|
||||
}
|
||||
|
||||
var policyDetails = await policyRepository.GetPolicyDetailsByOrganizationIdAsync(organizationId, policyType);
|
||||
var policyDetailGroups = policyDetails.GroupBy(policyDetail => policyDetail.UserId);
|
||||
var requirements = new List<T>();
|
||||
|
||||
foreach (var policyDetailGroup in policyDetailGroups)
|
||||
{
|
||||
var filteredPolicies = policyDetailGroup
|
||||
.Where(factory.Enforce)
|
||||
// Prevent deferred execution from causing inconsistent tests.
|
||||
.ToList();
|
||||
|
||||
requirements.Add(factory.Create(filteredPolicies));
|
||||
}
|
||||
|
||||
return requirements;
|
||||
}
|
||||
|
||||
public abstract Task OnSaveSideEffectsAsync(
|
||||
PolicyUpdate policyUpdate,
|
||||
Policy? currentPolicy
|
||||
);
|
||||
|
||||
public abstract Task<string> ValidateAsync(
|
||||
PolicyUpdate policyUpdate,
|
||||
Policy? currentPolicy
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user