Skip to content

Commit

Permalink
PT-504: Update Password checker to match AspNetCore.Identity options (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Egis committed Apr 1, 2021
1 parent ba46fd8 commit 8e537c1
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ public class PasswordValidationResult
/// </summary>
public bool PasswordViolatesMinLength { get; set; }

/// <summary>
/// Minimal unique chars count.
/// </summary>
public int MinUniqueCharsCount { get; set; }

/// <summary>
/// Indicates that password comprised of less than minimum number of unique chars.
/// </summary>
public bool PasswordViolatesMinUniqueCharsCount { get; set; }

/// <summary>
/// Indicates that entered password lacks one or more lower-case letters.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using VirtoCommerce.Platform.Core.Security;
Expand All @@ -10,8 +11,6 @@ namespace VirtoCommerce.Platform.Security.Services
/// </summary>
public class PasswordCheckService : IPasswordCheckService
{
protected char[] SpecialCharacters { get; } = { '!', '@', '#', '$', '%', '^', '&', '*', '?', '_', '~', '-', '£', '(', ')', '.', ',' };

protected IdentityOptions Options { get; }

/// <summary>
Expand All @@ -29,7 +28,8 @@ public virtual Task<PasswordValidationResult> ValidatePasswordAsync(string passw
var result = new PasswordValidationResult
{
PasswordIsValid = true,
MinPasswordLength = Options.Password.RequiredLength
MinPasswordLength = Options.Password.RequiredLength,
MinUniqueCharsCount = Options.Password.RequiredUniqueChars
};

if (!HasSufficientLength(password))
Expand All @@ -38,6 +38,12 @@ public virtual Task<PasswordValidationResult> ValidatePasswordAsync(string passw
result.PasswordViolatesMinLength = true;
}

if (!HasSufficientUniqueChars(password))
{
result.PasswordIsValid = false;
result.PasswordViolatesMinUniqueCharsCount = true;
}

if (Options.Password.RequireUppercase && !HasUpperCaseLetter(password))
{
result.PasswordIsValid = false;
Expand Down Expand Up @@ -71,6 +77,12 @@ protected virtual bool HasSufficientLength(string password)
&& password.Length >= Options.Password.RequiredLength;
}

private bool HasSufficientUniqueChars(string password)
{
return !string.IsNullOrEmpty(password)
&& password.Distinct().Count() >= Options.Password.RequiredUniqueChars;
}

protected virtual bool HasUpperCaseLetter(string password)
{
return !string.IsNullOrWhiteSpace(password)
Expand All @@ -92,7 +104,7 @@ protected virtual bool HasDigit(string password)
protected virtual bool HasSpecialCharacter(string password)
{
return !string.IsNullOrWhiteSpace(password)
&& password.IndexOfAny(SpecialCharacters) != -1;
&& !Regex.IsMatch(password, "^[a-zA-Z0-9]+$");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@
"invalid-token": "Reset password token is invalid or expired",
"password-too-weak": "New password does not comply one or more password security policies:",
"passwordViolatesMinLength": "Passwords must be {{minPasswordLength}} or more characters long",
"passwordViolatesMinUniqueCharsCount": "Passwords must use at least {{minUniqueCharsCount}} different characters",
"passwordMustHaveLowerCaseLetters": "Passwords must have at least one lowercase ('a'-'z')",
"passwordMustHaveUpperCaseLetters": "Passwords must have at least one uppercase ('A'-'Z')",
"passwordMustHaveDigits": "Passwords must have at least one digit ('0'-'9')",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ angular.module('platformWebApp')
forcePasswordChange: true,
passwordIsValid: true,
minPasswordLength: 0,
minUniqueCharsCount: 0,
errors: []
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ angular
var result = {
passwordIsValid: response.passwordIsValid,
minPasswordLength: response.minPasswordLength,
minUniqueCharsCount: response.minUniqueCharsCount,
errors: []
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class PasswordCheckServiceTests
[InlineData("LongPassword", true, true, false, false, 12, true, false, false, false, false, false)]
[InlineData("P455WithD1git5", true, true, true, false, 8, true, false, false, false, false, false)]
[InlineData("$ecur3P@Ssw0rd", true, true, true, true, 8, true, false, false, false, false, false)]
[InlineData("12ä45", false, false, false, true, 5, true, false, false, false, false, false)]
// Violation of minimal length
[InlineData("letmein", false, false, false, false, 8, false, true, false, false, false, false)]
[InlineData("123", false, false, false, false, 5, false, true, false, false, false, false)]
Expand Down

0 comments on commit 8e537c1

Please sign in to comment.