1
0
mirror of https://github.com/bitwarden/server synced 2026-01-29 07:43:22 +00:00
Files
server/src/Core/AdminConsole/Utilities/PolicyDataValidator.cs
Rui Tomé c8124667ee [PM-28842] Add validation to prevent excessive master password policy values (#6807)
* Enhance MasterPasswordPolicyData with validation attributes

Added data annotations for MinComplexity and MinLength properties to enforce validation rules. MinComplexity must be between 0 and 4, and MinLength must be between 12 and 128.

* Implement model validation in PolicyDataValidator and enhance error handling

Added a ValidateModel method to enforce validation rules for policy data. Updated error messages to provide clearer feedback on validation failures. Enhanced unit tests to cover new validation scenarios for MinLength and MinComplexity properties.

* Update PoliciesControllerTests to reflect new validation rules for MinComplexity and MinLength

Modified test cases to use updated values for MinComplexity (4) and MinLength (128). Added new tests to verify that excessive values for these properties return BadRequest responses. Ensured consistency across integration tests for both Admin and Public controllers.

* Enhance MasterPasswordPolicyData with XML documentation for properties

Added XML documentation comments for MinComplexity and MinLength properties to clarify their purpose and constraints. This improves code readability and provides better context for developers using the model.

* Add unit tests for PolicyDataValidator to validate minLength and minComplexity rules

Implemented new test cases to verify the behavior of the ValidateAndSerialize method in PolicyDataValidator. Tests cover scenarios for minimum and maximum values, as well as edge cases for invalid inputs, ensuring robust validation for MasterPassword policy data.
2026-01-26 11:38:06 +00:00

97 lines
3.6 KiB
C#

using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
namespace Bit.Core.AdminConsole.Utilities;
public static class PolicyDataValidator
{
/// <summary>
/// Validates and serializes policy data based on the policy type.
/// </summary>
/// <param name="data">The policy data to validate</param>
/// <param name="policyType">The type of policy</param>
/// <returns>Serialized JSON string if data is valid, null if data is null or empty</returns>
/// <exception cref="BadRequestException">Thrown when data validation fails</exception>
public static string? ValidateAndSerialize(Dictionary<string, object>? data, PolicyType policyType)
{
if (data == null || data.Count == 0)
{
return null;
}
try
{
var json = JsonSerializer.Serialize(data);
switch (policyType)
{
case PolicyType.MasterPassword:
var masterPasswordData = CoreHelpers.LoadClassFromJsonData<MasterPasswordPolicyData>(json);
ValidateModel(masterPasswordData, policyType);
break;
case PolicyType.SendOptions:
CoreHelpers.LoadClassFromJsonData<SendOptionsPolicyData>(json);
break;
case PolicyType.ResetPassword:
CoreHelpers.LoadClassFromJsonData<ResetPasswordDataModel>(json);
break;
}
return json;
}
catch (JsonException ex)
{
var fieldName = !string.IsNullOrEmpty(ex.Path) ? ex.Path.TrimStart('$', '.') : null;
var fieldInfo = !string.IsNullOrEmpty(fieldName) ? $": {fieldName} has an invalid value" : "";
throw new BadRequestException($"Invalid data for {policyType} policy{fieldInfo}.");
}
}
private static void ValidateModel(object model, PolicyType policyType)
{
var validationContext = new ValidationContext(model);
var validationResults = new List<ValidationResult>();
if (!Validator.TryValidateObject(model, validationContext, validationResults, true))
{
var errors = string.Join(", ", validationResults.Select(r => r.ErrorMessage));
throw new BadRequestException($"Invalid data for {policyType} policy: {errors}");
}
}
/// <summary>
/// Validates and deserializes policy metadata based on the policy type.
/// </summary>
/// <param name="metadata">The policy metadata to validate</param>
/// <param name="policyType">The type of policy</param>
/// <returns>Deserialized metadata model, or EmptyMetadataModel if metadata is null, empty, or validation fails</returns>
public static IPolicyMetadataModel ValidateAndDeserializeMetadata(Dictionary<string, object>? metadata, PolicyType policyType)
{
if (metadata == null || metadata.Count == 0)
{
return new EmptyMetadataModel();
}
try
{
var json = JsonSerializer.Serialize(metadata);
return policyType switch
{
PolicyType.OrganizationDataOwnership =>
CoreHelpers.LoadClassFromJsonData<OrganizationModelOwnershipPolicyModel>(json),
_ => new EmptyMetadataModel()
};
}
catch (JsonException)
{
return new EmptyMetadataModel();
}
}
}