Last active
July 18, 2024 17:22
-
-
Save makafanpeter/c7c393b3d5dd29d2aa001ef92fd24fde to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Text.RegularExpressions; | |
var pRequirement = new PasswordRequirement(8, true, true, true, true); | |
var strategies = new List<IPasswordRequirementStrategy> | |
{ | |
new MinimumLengthRequirementStrategy(pRequirement.MinimumLength), | |
}; | |
if (pRequirement.RequireUppercase) | |
{ | |
strategies.Add(new UppercaseRequirementStrategy()); | |
} | |
if (pRequirement.RequireDigit) | |
{ | |
strategies.Add(new DigitRequirementStrategy()); | |
} | |
if (pRequirement.RequireLowercase) | |
{ | |
strategies.Add(new LowercaseRequirementStrategy()); | |
} | |
if (pRequirement.RequireNonAlphanumeric) | |
{ | |
strategies.Add(new NonAlphanumericRequirementStrategy()); | |
} | |
var validationContext = new PasswordValidationContext(strategies); | |
var testPassword = "SecureP@ssword123"; | |
var validationResults = validationContext.ValidatePassword(testPassword); | |
Console.WriteLine(validationResults.ToString()); | |
public interface IPasswordRequirementStrategy | |
{ | |
SecurityResult Validate(string password); | |
} | |
public class NonAlphanumericRequirementStrategy : IPasswordRequirementStrategy | |
{ | |
public SecurityResult Validate(string password) | |
{ | |
Match specialCharacter = Regex.Match(password, @"^(?=.*[^\da-zA-Z])"); | |
if (!specialCharacter.Success) | |
{ | |
return SecurityResult.Failed("The password must contain at least one non-alphanumeric character."); | |
} | |
return SecurityResult.Success; | |
} | |
} | |
public class DigitRequirementStrategy : IPasswordRequirementStrategy | |
{ | |
public SecurityResult Validate(string password) | |
{ | |
Match digit = Regex.Match(password, @"^(?=.*\d)"); | |
if (!digit.Success) | |
{ | |
SecurityResult.Failed("The password must contain at least one digit."); | |
} | |
return SecurityResult.Success; | |
} | |
} | |
public class LowercaseRequirementStrategy : IPasswordRequirementStrategy | |
{ | |
public SecurityResult Validate(string password) | |
{ | |
Match lowercase = Regex.Match(password, @"^(?=.*[a-z])"); | |
return !lowercase.Success ? SecurityResult.Failed("The password must contain at least one lowercase character.") : SecurityResult.Success; | |
} | |
} | |
public class UppercaseRequirementStrategy : IPasswordRequirementStrategy | |
{ | |
public SecurityResult Validate(string password) | |
{ | |
Match uppercase = Regex.Match(password, @"^(?=.*[A-Z])"); | |
return !uppercase.Success ? SecurityResult.Failed("The password must contain at least one lowercase character.") : SecurityResult.Success; | |
} | |
} | |
public class MinimumLengthRequirementStrategy(int minimumLength) : IPasswordRequirementStrategy | |
{ | |
public SecurityResult Validate(string password) | |
{ | |
return password.Length >= minimumLength ? SecurityResult.Success : SecurityResult.Failed($"The password must be over {minimumLength} characters."); | |
} | |
} | |
public class PasswordValidationContext(IList<IPasswordRequirementStrategy> strategies) | |
{ | |
public SecurityResult ValidatePassword(string password) | |
{ | |
SecurityResult result; | |
if (string.IsNullOrEmpty(password)) | |
{ | |
result = SecurityResult.Failed("The password cannot be empty"); | |
return result; | |
} | |
foreach (var strategy in strategies) | |
{ | |
result = strategy.Validate(password); | |
if (!result.Succeeded) | |
{ | |
return result; | |
} | |
} | |
return SecurityResult.Success; | |
} | |
} | |
public class SecurityResult | |
{ | |
private static readonly SecurityResult _success = new SecurityResult { Succeeded = true }; | |
private readonly List<string> _errors = new List<string>(); | |
/// <summary> | |
/// Flag indicating whether if the operation succeeded or not. | |
/// </summary> | |
/// <value>True if the operation succeeded, otherwise false.</value> | |
public bool Succeeded { get; protected set; } | |
/// <summary> | |
/// An <see cref="IEnumerable{T}"/> of <see cref="SecurityResult"/>s containing an errors | |
/// that occurred during the identity operation. | |
/// </summary> | |
/// <value>An <see cref="IEnumerable{T}"/> of <see cref="SecurityResult"/>s.</value> | |
public IEnumerable<string> Errors => _errors; | |
/// <summary> | |
/// Returns an <see cref="SecurityResult"/> indicating a successful identity operation. | |
/// </summary> | |
/// <returns>An <see cref="SecurityResult"/> indicating a successful operation.</returns> | |
public static SecurityResult Success => _success; | |
/// <summary> | |
/// Creates an <see cref="SecurityResult"/> indicating a failed identity operation, with a list of <paramref name="errors"/> if applicable. | |
/// </summary> | |
/// <param name="errors">An optional array of <see cref="SecurityResult"/>s which caused the operation to fail.</param> | |
/// <returns>An <see cref="SecurityResult"/> indicating a failed identity operation, with a list of <paramref name="errors"/> if applicable.</returns> | |
public static SecurityResult Failed(params string[] errors) | |
{ | |
var result = new SecurityResult { Succeeded = false }; | |
if (errors != null) | |
{ | |
result._errors.AddRange(errors); | |
} | |
return result; | |
} | |
public static SecurityResult Failed(string errors) | |
{ | |
var result = new SecurityResult { Succeeded = false }; | |
if (errors != null) | |
{ | |
result._errors.Add(errors); | |
} | |
return result; | |
} | |
/// <summary> | |
/// Converts the value of the current <see cref="SecurityResult"/> object to its equivalent string representation. | |
/// </summary> | |
/// <returns>A string representation of the current <see cref="SecurityResult"/> object.</returns> | |
/// <remarks> | |
/// If the operation was successful the ToString() will return "Succeeded" otherwise it returned | |
/// "Failed : " followed by a comma delimited list of error codes from its <see cref="Errors"/> collection, if any. | |
/// </remarks> | |
public override string ToString() | |
{ | |
return Succeeded ? "Succeeded" : $"Failed : {string.Join(",", Errors.Select(x => x).ToList())}"; | |
} | |
} | |
public record PasswordRequirement( | |
int MinimumLength, | |
bool RequireUppercase, | |
bool RequireLowercase, | |
bool RequireNonAlphanumeric, | |
bool RequireDigit | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment