mirror of
https://github.com/bitwarden/server
synced 2025-12-23 03:33:35 +00:00
[PM-18239] Master password policy requirement (#5936)
* wip * initial implementation * add tests * more tests, fix policy Enabled * remove exempt statuses * test EnforcedOptions is populated * clean up, add test * fix test, add json attributes for deserialization * fix attribute casing * fix test --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
@@ -1,20 +1,28 @@
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using System.Text.Json.Serialization;
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
|
||||
public class MasterPasswordPolicyData : IPolicyDataModel
|
||||
{
|
||||
[JsonPropertyName("minComplexity")]
|
||||
public int? MinComplexity { get; set; }
|
||||
[JsonPropertyName("minLength")]
|
||||
public int? MinLength { get; set; }
|
||||
[JsonPropertyName("requireLower")]
|
||||
public bool? RequireLower { get; set; }
|
||||
[JsonPropertyName("requireUpper")]
|
||||
public bool? RequireUpper { get; set; }
|
||||
[JsonPropertyName("requireNumbers")]
|
||||
public bool? RequireNumbers { get; set; }
|
||||
[JsonPropertyName("requireSpecial")]
|
||||
public bool? RequireSpecial { get; set; }
|
||||
[JsonPropertyName("enforceOnLogin")]
|
||||
public bool? EnforceOnLogin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Combine the other policy data with this instance, taking the most secure options
|
||||
/// </summary>
|
||||
/// <param name="other">The other policy instance to combine with this</param>
|
||||
public void CombineWith(MasterPasswordPolicyData other)
|
||||
public void CombineWith(MasterPasswordPolicyData? other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
|
||||
/// <summary>
|
||||
/// Policy requirements for the Master Password Requirements policy.
|
||||
/// </summary>
|
||||
public class MasterPasswordPolicyRequirement : IPolicyRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether MasterPassword requirements are enabled for the user.
|
||||
/// </summary>
|
||||
public bool Enabled { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Master Password Policy data model associated with this Policy
|
||||
/// </summary>
|
||||
public MasterPasswordPolicyData? EnforcedOptions { get; init; }
|
||||
}
|
||||
|
||||
public class MasterPasswordPolicyRequirementFactory : BasePolicyRequirementFactory<MasterPasswordPolicyRequirement>
|
||||
{
|
||||
public override PolicyType PolicyType => PolicyType.MasterPassword;
|
||||
|
||||
protected override bool ExemptProviders => false;
|
||||
|
||||
protected override IEnumerable<OrganizationUserType> ExemptRoles => [];
|
||||
|
||||
protected override IEnumerable<OrganizationUserStatusType> ExemptStatuses =>
|
||||
[OrganizationUserStatusType.Accepted,
|
||||
OrganizationUserStatusType.Invited,
|
||||
OrganizationUserStatusType.Revoked,
|
||||
];
|
||||
|
||||
public override MasterPasswordPolicyRequirement Create(IEnumerable<PolicyDetails> policyDetails)
|
||||
{
|
||||
var result = policyDetails
|
||||
.Select(p => p.GetDataModel<MasterPasswordPolicyData>())
|
||||
.Aggregate(
|
||||
new MasterPasswordPolicyRequirement(),
|
||||
(result, data) =>
|
||||
{
|
||||
data.CombineWith(result.EnforcedOptions);
|
||||
return new MasterPasswordPolicyRequirement
|
||||
{
|
||||
Enabled = true,
|
||||
EnforcedOptions = data
|
||||
};
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
@@ -19,21 +21,39 @@ public class PolicyService : IPolicyService
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
||||
|
||||
public PolicyService(
|
||||
IApplicationCacheService applicationCacheService,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IPolicyRepository policyRepository,
|
||||
GlobalSettings globalSettings)
|
||||
GlobalSettings globalSettings,
|
||||
IFeatureService featureService,
|
||||
IPolicyRequirementQuery policyRequirementQuery)
|
||||
{
|
||||
_applicationCacheService = applicationCacheService;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_policyRepository = policyRepository;
|
||||
_globalSettings = globalSettings;
|
||||
_featureService = featureService;
|
||||
_policyRequirementQuery = policyRequirementQuery;
|
||||
}
|
||||
|
||||
public async Task<MasterPasswordPolicyData> GetMasterPasswordPolicyForUserAsync(User user)
|
||||
{
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements))
|
||||
{
|
||||
var masterPaswordPolicy = (await _policyRequirementQuery.GetAsync<MasterPasswordPolicyRequirement>(user.Id));
|
||||
|
||||
if (!masterPaswordPolicy.Enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return masterPaswordPolicy.EnforcedOptions;
|
||||
}
|
||||
|
||||
var policies = (await _policyRepository.GetManyByUserIdAsync(user.Id))
|
||||
.Where(p => p.Type == PolicyType.MasterPassword && p.Enabled)
|
||||
.ToList();
|
||||
@@ -51,6 +71,7 @@ public class PolicyService : IPolicyService
|
||||
}
|
||||
|
||||
return enforcedOptions;
|
||||
|
||||
}
|
||||
|
||||
public async Task<ICollection<OrganizationUserPolicyDetails>> GetPoliciesApplicableToUserAsync(Guid userId, PolicyType policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted)
|
||||
|
||||
Reference in New Issue
Block a user