V9: Fix for migration of non-default configurated users/members (#11684)
* https://github.com/umbraco/Umbraco-CMS/issues/11366 Fallback to try login using super legacy HMACSHA1 even when the algorithm is stated as being HMACSHA256. The issue is that v8 saves HMACSHA256 on the user, but when configured to use legacy encoding it actually uses HMACSHA1 * Support migration of members with: UseLegacyEncoding+Clear UseLegacyEncoding+Encrypted (Requires machine key) UseLegacyEncoding+Hashed * Fixes unit tests * Avoid exceptions + unit tests * Save unknown algorithm if we dont know it, instead of persisting a wrong algorithm. * Added setting to enable clear text password rehashes. * Removed support for migration of clear text passwords * Fixed unit test
This commit is contained in:
@@ -82,6 +82,7 @@ namespace JsonSchema
|
||||
public BasicAuthSettings BasicAuth { get; set; }
|
||||
|
||||
public PackageMigrationSettings PackageMigration { get; set; }
|
||||
public LegacyPasswordMigrationSettings LegacyPasswordMigration { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Cms.Core.Configuration.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Typed configuration options for legacy machine key settings used for migration of members from a v8 solution.
|
||||
/// </summary>
|
||||
[UmbracoOptions(Constants.Configuration.ConfigLegacyPasswordMigration)]
|
||||
public class LegacyPasswordMigrationSettings
|
||||
{
|
||||
private const string StaticDecryptionKey = "";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the decryption algorithm.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Currently only AES is supported. This should include all machine keys generated by Umbraco.
|
||||
/// </remarks>
|
||||
public string MachineKeyDecryption => "AES";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the decryption hex-formatted string key found in legacy web.config machineKey configuration-element.
|
||||
/// </summary>
|
||||
[DefaultValue(StaticDecryptionKey)]
|
||||
public string MachineKeyDecryptionKey { get; set; } = StaticDecryptionKey;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
public const string ConfigHostingDebug = ConfigHostingPrefix + "Debug";
|
||||
public const string ConfigCustomErrorsMode = ConfigCustomErrorsPrefix + "Mode";
|
||||
public const string ConfigActiveDirectory = ConfigPrefix + "ActiveDirectory";
|
||||
public const string ConfigLegacyPasswordMigration = ConfigPrefix + "LegacyPasswordMigration";
|
||||
public const string ConfigContent = ConfigPrefix + "Content";
|
||||
public const string ConfigCoreDebug = ConfigCorePrefix + "Debug";
|
||||
public const string ConfigExceptionFilter = ConfigPrefix + "ExceptionFilter";
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace Umbraco.Cms.Core
|
||||
public const string AspNetCoreV2PasswordHashAlgorithmName = "PBKDF2.ASPNETCORE.V2";
|
||||
public const string AspNetUmbraco8PasswordHashAlgorithmName = "HMACSHA256";
|
||||
public const string AspNetUmbraco4PasswordHashAlgorithmName = "HMACSHA1";
|
||||
public const string UnknownPasswordConfigJson = "{\"hashAlgorithm\":\"Unknown\"}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
.AddUmbracoOptions<RichTextEditorSettings>()
|
||||
.AddUmbracoOptions<BasicAuthSettings>()
|
||||
.AddUmbracoOptions<RuntimeMinificationSettings>()
|
||||
.AddUmbracoOptions<LegacyPasswordMigrationSettings>()
|
||||
.AddUmbracoOptions<PackageMigrationSettings>();
|
||||
|
||||
return builder;
|
||||
|
||||
@@ -50,9 +50,33 @@ namespace Umbraco.Cms.Core.Security
|
||||
if (dbPassword.StartsWith(Constants.Security.EmptyPasswordPrefix))
|
||||
return false;
|
||||
|
||||
var storedHashedPass = ParseStoredHashPassword(algorithm, dbPassword, out var salt);
|
||||
var hashed = HashPassword(algorithm, password, salt);
|
||||
return storedHashedPass == hashed;
|
||||
try
|
||||
{
|
||||
var storedHashedPass = ParseStoredHashPassword(algorithm, dbPassword, out var salt);
|
||||
var hashed = HashPassword(algorithm, password, salt);
|
||||
return storedHashedPass == hashed;
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
//This can happen if the length of the password is wrong and a salt cannot be extracted.
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify a legacy hashed password (HMACSHA1)
|
||||
/// </summary>
|
||||
public bool VerifyLegacyHashedPassword(string password, string dbPassword)
|
||||
{
|
||||
var hashAlgorith = new HMACSHA1
|
||||
{
|
||||
//the legacy salt was actually the password :(
|
||||
Key = Encoding.Unicode.GetBytes(password)
|
||||
};
|
||||
var hashed = Convert.ToBase64String(hashAlgorith.ComputeHash(Encoding.Unicode.GetBytes(password)));
|
||||
|
||||
return dbPassword == hashed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -774,6 +774,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
{
|
||||
memberDto.PasswordConfig = DefaultPasswordConfigJson;
|
||||
changedCols.Add("passwordConfig");
|
||||
}else if (memberDto.PasswordConfig == Constants.Security.UnknownPasswordConfigJson)
|
||||
{
|
||||
changedCols.Add("passwordConfig");
|
||||
}
|
||||
|
||||
// do NOT update the password if it has not changed or if it is null or empty
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
@@ -16,9 +22,27 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// </remarks>
|
||||
public class MemberPasswordHasher : UmbracoPasswordHasher<MemberIdentityUser>
|
||||
{
|
||||
private readonly IOptions<LegacyPasswordMigrationSettings> _legacyMachineKeySettings;
|
||||
private readonly ILogger<MemberPasswordHasher> _logger;
|
||||
|
||||
[Obsolete("Use ctor with all params")]
|
||||
public MemberPasswordHasher(LegacyPasswordSecurity legacyPasswordHasher, IJsonSerializer jsonSerializer)
|
||||
: this(legacyPasswordHasher,
|
||||
jsonSerializer,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IOptions<LegacyPasswordMigrationSettings>>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<ILogger<MemberPasswordHasher>>())
|
||||
{
|
||||
}
|
||||
|
||||
public MemberPasswordHasher(
|
||||
LegacyPasswordSecurity legacyPasswordHasher,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IOptions<LegacyPasswordMigrationSettings> legacyMachineKeySettings,
|
||||
ILogger<MemberPasswordHasher> logger)
|
||||
: base(legacyPasswordHasher, jsonSerializer)
|
||||
{
|
||||
_legacyMachineKeySettings = legacyMachineKeySettings;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -36,10 +60,21 @@ namespace Umbraco.Cms.Core.Security
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
var isPasswordAlgorithmKnown = user.PasswordConfig.IsNullOrWhiteSpace() == false &&
|
||||
user.PasswordConfig != Constants.Security.UnknownPasswordConfigJson;
|
||||
// if there's password config use the base implementation
|
||||
if (!user.PasswordConfig.IsNullOrWhiteSpace())
|
||||
if (isPasswordAlgorithmKnown)
|
||||
{
|
||||
return base.VerifyHashedPassword(user, hashedPassword, providedPassword);
|
||||
var result = base.VerifyHashedPassword(user, hashedPassword, providedPassword);
|
||||
if (result != PasswordVerificationResult.Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// We need to check for clear text passwords from members as the first thing. This was possible in v8 :(
|
||||
else if (IsSuccessfulLegacyPassword(hashedPassword, providedPassword))
|
||||
{
|
||||
return PasswordVerificationResult.SuccessRehashNeeded;
|
||||
}
|
||||
|
||||
// Else we need to detect what the password is. This will be the case
|
||||
@@ -66,7 +101,16 @@ namespace Umbraco.Cms.Core.Security
|
||||
return base.VerifyHashedPassword(user, hashedPassword, providedPassword);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("unable to determine member password hashing algorith");
|
||||
if (isPasswordAlgorithmKnown)
|
||||
{
|
||||
_logger.LogError("Unable to determine member password hashing algorithm");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Unable to determine member password hashing algorithm, but this can happen when member enters a wrong password, before it has be rehashed");
|
||||
}
|
||||
|
||||
return PasswordVerificationResult.Failed;
|
||||
}
|
||||
|
||||
var isValid = LegacyPasswordSecurity.VerifyPassword(
|
||||
@@ -76,5 +120,65 @@ namespace Umbraco.Cms.Core.Security
|
||||
|
||||
return isValid ? PasswordVerificationResult.SuccessRehashNeeded : PasswordVerificationResult.Failed;
|
||||
}
|
||||
|
||||
private bool IsSuccessfulLegacyPassword(string hashedPassword, string providedPassword)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_legacyMachineKeySettings.Value.MachineKeyDecryptionKey))
|
||||
{
|
||||
try
|
||||
{
|
||||
var decryptedPassword = DecryptLegacyPassword(hashedPassword, _legacyMachineKeySettings.Value.MachineKeyDecryption, _legacyMachineKeySettings.Value.MachineKeyDecryptionKey);
|
||||
return decryptedPassword == providedPassword;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not decrypt password even that a DecryptionKey is provided. This means the DecryptionKey is wrong.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var result = LegacyPasswordSecurity.VerifyPassword(Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName, providedPassword, hashedPassword);
|
||||
return result || LegacyPasswordSecurity.VerifyPassword(Constants.Security.AspNetUmbraco4PasswordHashAlgorithmName, providedPassword, hashedPassword);
|
||||
}
|
||||
|
||||
private static string DecryptLegacyPassword(string encryptedPassword, string algorithmName, string decryptionKey)
|
||||
{
|
||||
SymmetricAlgorithm algorithm;
|
||||
switch (algorithmName)
|
||||
{
|
||||
case "AES":
|
||||
algorithm = new AesCryptoServiceProvider()
|
||||
{
|
||||
Key = StringToByteArray(decryptionKey),
|
||||
IV = new byte[16]
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"The algorithm ({algorithmName}) is not supported");
|
||||
}
|
||||
|
||||
using (algorithm)
|
||||
{
|
||||
return DecryptLegacyPassword(encryptedPassword, algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
private static string DecryptLegacyPassword(string encryptedPassword, SymmetricAlgorithm algorithm)
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
ICryptoTransform cryptoTransform = algorithm.CreateDecryptor();
|
||||
var cryptoStream = new CryptoStream((Stream)memoryStream, cryptoTransform, CryptoStreamMode.Write);
|
||||
var buf = Convert.FromBase64String(encryptedPassword);
|
||||
cryptoStream.Write(buf, 0, 32);
|
||||
cryptoStream.FlushFinalBlock();
|
||||
|
||||
return Encoding.Unicode.GetString(memoryStream.ToArray());
|
||||
}
|
||||
|
||||
private static byte[] StringToByteArray(string hex) =>
|
||||
Enumerable.Range(0, hex.Length)
|
||||
.Where(x => x % 2 == 0)
|
||||
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,6 +592,12 @@ namespace Umbraco.Cms.Core.Security
|
||||
member.PasswordConfiguration = identityUser.PasswordConfig;
|
||||
}
|
||||
|
||||
if (member.PasswordConfiguration != identityUser.PasswordConfig)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
member.PasswordConfiguration = identityUser.PasswordConfig;
|
||||
}
|
||||
|
||||
if (member.SecurityStamp != identityUser.SecurityStamp)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
|
||||
@@ -53,6 +53,13 @@ namespace Umbraco.Cms.Core.Security
|
||||
if (LegacyPasswordSecurity.SupportHashAlgorithm(deserialized.HashAlgorithm))
|
||||
{
|
||||
var result = LegacyPasswordSecurity.VerifyPassword(deserialized.HashAlgorithm, providedPassword, hashedPassword);
|
||||
|
||||
//We need to special handle this case, apparently v8 still saves the user algorithm as {"hashAlgorithm":"HMACSHA256"}, when using legacy encoding and hasinging.
|
||||
if (result == false)
|
||||
{
|
||||
result = LegacyPasswordSecurity.VerifyLegacyHashedPassword(providedPassword, hashedPassword);
|
||||
}
|
||||
|
||||
return result
|
||||
? PasswordVerificationResult.SuccessRehashNeeded
|
||||
: PasswordVerificationResult.Failed;
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
string password = _passwordGenerator.GeneratePassword();
|
||||
return password;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used to validate the password without an identity user
|
||||
/// Validation code is based on the default ValidatePasswordAsync code
|
||||
@@ -205,6 +205,8 @@ namespace Umbraco.Cms.Core.Security
|
||||
|
||||
await lockoutStore.ResetAccessFailedCountAsync(user, CancellationToken.None);
|
||||
|
||||
//Ensure the password config is null, so it is set to the default in repository
|
||||
user.PasswordConfig = null;
|
||||
return await UpdateAsync(user);
|
||||
}
|
||||
|
||||
@@ -234,6 +236,11 @@ namespace Umbraco.Cms.Core.Security
|
||||
// here we are persisting the value for the back office
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(user.PasswordConfig))
|
||||
{
|
||||
//We cant pass null as that would be interpreted as the default algoritm, but due to the failing attempt we dont know.
|
||||
user.PasswordConfig = Constants.Security.UnknownPasswordConfigJson;
|
||||
}
|
||||
IdentityResult result = await UpdateAsync(user);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Infrastructure.Security;
|
||||
using Umbraco.Cms.Infrastructure.Serialization;
|
||||
@@ -10,65 +14,35 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Security
|
||||
[TestFixture]
|
||||
public class MemberPasswordHasherTests
|
||||
{
|
||||
private MemberPasswordHasher CreateSut() => new MemberPasswordHasher(new LegacyPasswordSecurity(), new JsonNetSerializer());
|
||||
|
||||
[Test]
|
||||
public void VerifyHashedPassword_GivenAnAspNetIdentity2PasswordHash_ThenExpectSuccessRehashNeeded()
|
||||
[TestCase("Password123!", "AQAAAAEAACcQAAAAEGF/tTVoL6ef3bQPZFYfbgKFu1CDQIAMgyY1N4EDt9jqdG/hsOX93X1U6LNvlIQ3mw==", null, ExpectedResult = PasswordVerificationResult.Success, Description = "AspNetCoreIdentityPasswordHash: Correct password")]
|
||||
[TestCase("wrongPassword", "AQAAAAEAACcQAAAAEGF/tTVoL6ef3bQPZFYfbgKFu1CDQIAMgyY1N4EDt9jqdG/hsOX93X1U6LNvlIQ3mw==", null, ExpectedResult = PasswordVerificationResult.Failed, Description = "AspNetCoreIdentityPasswordHash: Wrong password")]
|
||||
[TestCase("Password123!", "yDiU2YyuYZU4jz6F0fpErQ==BxNRHkXBVyJs9gwWF6ktWdfDwYf5bwm+rvV7tOcNNx8=", null, ExpectedResult = PasswordVerificationResult.SuccessRehashNeeded, Description = "GivenALegacyPasswordHash: Correct password")]
|
||||
[TestCase("wrongPassword", "yDiU2YyuYZU4jz6F0fpErQ==BxNRHkXBVyJs9gwWF6ktWdfDwYf5bwm+rvV7tOcNNx8=", null, ExpectedResult = PasswordVerificationResult.Failed, Description = "GivenALegacyPasswordHash: Wrong password")]
|
||||
[TestCase("Password123!", "AJszAsQqxOYbASKfL3JVUu6cjU18ouizXDfX4j7wLlir8SWj2yQaTepE9e5bIohIsQ==", null, ExpectedResult = PasswordVerificationResult.SuccessRehashNeeded, Description = "GivenALegacyPasswordHash: Correct password")]
|
||||
[TestCase("wrongPassword", "AJszAsQqxOYbASKfL3JVUu6cjU18ouizXDfX4j7wLlir8SWj2yQaTepE9e5bIohIsQ==", null, ExpectedResult = PasswordVerificationResult.Failed, Description = "GivenALegacyPasswordHash: Wrong password")]
|
||||
[TestCase("1234567890", "1234567890", null, ExpectedResult = PasswordVerificationResult.Failed, Description = "ClearText: Correct password, but not supported")]
|
||||
[TestCase("wrongPassword", "1234567890", null, ExpectedResult = PasswordVerificationResult.Failed, Description = "ClearText: Wrong password")]
|
||||
[TestCase("1234567890", "XyFRG4/xJ5JGQJYqqIFK70BjHdM=", null, ExpectedResult = PasswordVerificationResult.SuccessRehashNeeded, Description = "Hashed: Correct password")]
|
||||
[TestCase("wrongPassword", "XyFRG4/xJ5JGQJYqqIFK70BjHdM=", null, ExpectedResult = PasswordVerificationResult.Failed, Description = "Hashed: Wrong password")]
|
||||
[TestCase("1234567890", "K2JPOhoqNoysfnnD67QsWDSliHrjoSTRTvv9yiaKf30=", "1D43BFA074DF6DCEF6E44A7F5B5F56CDDD60BE198FBBB0222C96A5BD696F3CAA", ExpectedResult = PasswordVerificationResult.SuccessRehashNeeded, Description = "Encrypted: Correct password and correct decryptionKey")]
|
||||
[TestCase("wrongPassword", "K2JPOhoqNoysfnnD67QsWDSliHrjoSTRTvv9yiaKf30=", "1D43BFA074DF6DCEF6E44A7F5B5F56CDDD60BE198FBBB0222C96A5BD696F3CAA", ExpectedResult = PasswordVerificationResult.Failed, Description = "Encrypted: Wrong password but correct decryptionKey")]
|
||||
[TestCase("1234567890", "qiuwRr4K7brpTcIzLFfR3iGG9zj4/z4ewHCVZmYUDKM=", "B491B602E0CE1D52450A8089FD2013B340743A7EFCC12B039BD11977A083ACA1", ExpectedResult = PasswordVerificationResult.Failed, Description = "Encrypted: Correct password but wrong decryptionKey")]
|
||||
[TestCase("1234567890", "qiuwRr4K7brpTcIzLFfR3iGG9zj4/z4ewHCVZmYUDKM=", "InvalidDecryptionKey", ExpectedResult = PasswordVerificationResult.Failed, Description = "Encrypted: Invalid decryptionKey")]
|
||||
public PasswordVerificationResult VerifyHashedPassword(string password, string encryptedPassword, string decryptionKey)
|
||||
{
|
||||
const string password = "Password123!";
|
||||
const string hash = "AJszAsQqxOYbASKfL3JVUu6cjU18ouizXDfX4j7wLlir8SWj2yQaTepE9e5bIohIsQ==";
|
||||
var member = new MemberIdentityUser() { PasswordConfig = null };
|
||||
|
||||
var sut = CreateSut();
|
||||
var result = sut.VerifyHashedPassword(new MemberIdentityUser(), hash, password);
|
||||
var sut = new MemberPasswordHasher(
|
||||
new LegacyPasswordSecurity(),
|
||||
new JsonNetSerializer(),
|
||||
Options.Create<LegacyPasswordMigrationSettings>(new LegacyPasswordMigrationSettings()
|
||||
{
|
||||
MachineKeyDecryptionKey = decryptionKey
|
||||
}),
|
||||
NullLoggerFactory.Instance.CreateLogger<MemberPasswordHasher>());
|
||||
|
||||
Assert.AreEqual(result, PasswordVerificationResult.SuccessRehashNeeded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyHashedPassword_GivenAnAspNetCoreIdentityPasswordHash_ThenExpectSuccess()
|
||||
{
|
||||
const string password = "Password123!";
|
||||
const string hash = "AQAAAAEAACcQAAAAEGF/tTVoL6ef3bQPZFYfbgKFu1CDQIAMgyY1N4EDt9jqdG/hsOX93X1U6LNvlIQ3mw==";
|
||||
|
||||
var sut = CreateSut();
|
||||
var result = sut.VerifyHashedPassword(new MemberIdentityUser(), hash, password);
|
||||
|
||||
Assert.AreEqual(result, PasswordVerificationResult.Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyHashedPassword_GivenALegacyPasswordHash_ThenExpectSuccessRehashNeeded()
|
||||
{
|
||||
const string password = "Password123!";
|
||||
const string hash = "yDiU2YyuYZU4jz6F0fpErQ==BxNRHkXBVyJs9gwWF6ktWdfDwYf5bwm+rvV7tOcNNx8=";
|
||||
|
||||
var sut = CreateSut();
|
||||
var result = sut.VerifyHashedPassword(new MemberIdentityUser(), hash, password);
|
||||
|
||||
Assert.AreEqual(result, PasswordVerificationResult.SuccessRehashNeeded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyHashedPassword_GivenAnUnknownBase64Hash_ThenExpectInvalidOperationException()
|
||||
{
|
||||
var hashBytes = new byte[] {3, 2, 1};
|
||||
var hash = Convert.ToBase64String(hashBytes);
|
||||
|
||||
var sut = CreateSut();
|
||||
Assert.Throws<InvalidOperationException>(() => sut.VerifyHashedPassword(new MemberIdentityUser(), hash, "password"));
|
||||
}
|
||||
|
||||
[TestCase("AJszAsQqxOYbASKfL3JVUu6cjU18ouizXDfX4j7wLlir8SWj2yQaTepE9e5bIohIsQ==")]
|
||||
[TestCase("AQAAAAEAACcQAAAAEGF/tTVoL6ef3bQPZFYfbgKFu1CDQIAMgyY1N4EDt9jqdG/hsOX93X1U6LNvlIQ3mw==")]
|
||||
[TestCase("yDiU2YyuYZU4jz6F0fpErQ==BxNRHkXBVyJs9gwWF6ktWdfDwYf5bwm+rvV7tOcNNx8=")]
|
||||
public void VerifyHashedPassword_GivenAnInvalidPassword_ThenExpectFailure(string hash)
|
||||
{
|
||||
const string invalidPassword = "nope";
|
||||
|
||||
var sut = CreateSut();
|
||||
var result = sut.VerifyHashedPassword(new MemberIdentityUser(), hash, invalidPassword);
|
||||
|
||||
Assert.AreEqual(result, PasswordVerificationResult.Failed);
|
||||
return sut.VerifyHashedPassword(member, encryptedPassword, password);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user