diff --git a/src/Umbraco.Core/Constants-Security.cs b/src/Umbraco.Core/Constants-Security.cs
index fda4c23dc0..9bcdedca70 100644
--- a/src/Umbraco.Core/Constants-Security.cs
+++ b/src/Umbraco.Core/Constants-Security.cs
@@ -62,6 +62,7 @@
public const string AspNetCoreV3PasswordHashAlgorithmName = "PBKDF2.ASPNETCORE.V3";
public const string AspNetCoreV2PasswordHashAlgorithmName = "PBKDF2.ASPNETCORE.V2";
public const string AspNetUmbraco8PasswordHashAlgorithmName = "HMACSHA256";
+ public const string AspNetUmbraco4PasswordHashAlgorithmName = "HMACSHA1";
}
}
}
diff --git a/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs b/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs
index 9b14c3ccba..d72ecda56c 100644
--- a/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs
+++ b/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using System.Security.Cryptography;
using System.Text;
using Umbraco.Core.Configuration;
@@ -11,66 +12,115 @@ namespace Umbraco.Core.Security
///
public class LegacyPasswordSecurity
{
- // TODO: This class no longer has the logic available to verify the old old old password format, we should
- // include this ability so that upgrades for very old versions/data can work and then auto-migrate to the new password format.
-
- private readonly IPasswordConfiguration _passwordConfiguration;
- private readonly PasswordGenerator _generator;
-
- ///
- /// Constructor
- ///
- ///
- public LegacyPasswordSecurity(IPasswordConfiguration passwordConfiguration)
- {
- _passwordConfiguration = passwordConfiguration;
- _generator = new PasswordGenerator(passwordConfiguration);
- }
-
- public string GeneratePassword() => _generator.GeneratePassword();
-
- ///
- /// Returns a hashed password value used to store in a data store
- ///
- ///
- ///
- public string HashPasswordForStorage(string password)
+ // Used for tests
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string HashPasswordForStorage(string algorithmType, string password)
{
if (string.IsNullOrWhiteSpace(password))
throw new ArgumentException("password cannot be empty", nameof(password));
string salt;
- var hashed = HashNewPassword(password, out salt);
- return FormatPasswordForStorage(hashed, salt);
+ var hashed = HashNewPassword(algorithmType, password, out salt);
+ return FormatPasswordForStorage(algorithmType, hashed, salt);
+ }
+
+ // Used for tests
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string FormatPasswordForStorage(string algorithmType, string hashedPassword, string salt)
+ {
+ if (IsLegacySHA1Algorithm(algorithmType))
+ {
+ return hashedPassword;
+ }
+
+ return salt + hashedPassword;
}
///
- /// If the password format is a hashed keyed algorithm then we will pre-pend the salt used to hash the password
- /// to the hashed password itself.
+ /// Verifies if the password matches the expected hash+salt of the stored password string
///
- ///
+ /// The hashing algorithm for the stored password.
+ /// The password.
+ /// The value of the password stored in a data store.
+ ///
+ public bool VerifyPassword(string algorithm, string password, string dbPassword)
+ {
+ if (string.IsNullOrWhiteSpace(dbPassword)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(dbPassword));
+
+ 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;
+ }
+
+ ///
+ /// Create a new password hash and a new salt
+ ///
+ /// The hashing algorithm for the password.
+ ///
///
///
- public string FormatPasswordForStorage(string hashedPassword, string salt)
+ // TODO: Do we need this method? We shouldn't be using this class to create new password hashes for storage
+ public string HashNewPassword(string algorithm, string newPassword, out string salt)
{
- return salt + hashedPassword;
+ salt = GenerateSalt();
+ return HashPassword(algorithm, newPassword, salt);
+ }
+
+ ///
+ /// Parses out the hashed password and the salt from the stored password string value
+ ///
+ /// The hashing algorithm for the stored password.
+ ///
+ /// returns the salt
+ ///
+ public string ParseStoredHashPassword(string algorithm, string storedString, out string salt)
+ {
+ if (string.IsNullOrWhiteSpace(storedString)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(storedString));
+
+ // This is for the <= v4 hashing algorithm for which there was no salt
+ if (IsLegacySHA1Algorithm(algorithm))
+ {
+ salt = string.Empty;
+ return storedString;
+ }
+
+
+ var saltLen = GenerateSalt();
+ salt = storedString.Substring(0, saltLen.Length);
+ return storedString.Substring(saltLen.Length);
+ }
+
+ public static string GenerateSalt()
+ {
+ var numArray = new byte[16];
+ new RNGCryptoServiceProvider().GetBytes(numArray);
+ return Convert.ToBase64String(numArray);
}
///
/// Hashes a password with a given salt
///
+ /// The hashing algorithm for the password.
///
///
///
- public string HashPassword(string pass, string salt)
+ private string HashPassword(string algorithmType, string pass, string salt)
{
+ if (IsLegacySHA1Algorithm(algorithmType))
+ {
+ return HashLegacySHA1Password(pass);
+ }
+
//This is the correct way to implement this (as per the sql membership provider)
var bytes = Encoding.Unicode.GetBytes(pass);
var saltBytes = Convert.FromBase64String(salt);
byte[] inArray;
- var hashAlgorithm = GetCurrentHashAlgorithm();
+ var hashAlgorithm = GetHashAlgorithm(algorithmType);
var algorithm = hashAlgorithm as KeyedHashAlgorithm;
if (algorithm != null)
{
@@ -113,83 +163,64 @@ namespace Umbraco.Core.Security
return Convert.ToBase64String(inArray);
}
- ///
- /// Verifies if the password matches the expected hash+salt of the stored password string
- ///
- /// The password.
- /// The value of the password stored in a data store.
- ///
- public bool VerifyPassword(string password, string dbPassword)
- {
- if (string.IsNullOrWhiteSpace(dbPassword)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(dbPassword));
-
- if (dbPassword.StartsWith(Constants.Security.EmptyPasswordPrefix))
- return false;
-
- var storedHashedPass = ParseStoredHashPassword(dbPassword, out var salt);
- var hashed = HashPassword(password, salt);
- return storedHashedPass == hashed;
- }
-
- ///
- /// Create a new password hash and a new salt
- ///
- ///
- ///
- ///
- public string HashNewPassword(string newPassword, out string salt)
- {
- salt = GenerateSalt();
- return HashPassword(newPassword, salt);
- }
-
- ///
- /// Parses out the hashed password and the salt from the stored password string value
- ///
- ///
- /// returns the salt
- ///
- public string ParseStoredHashPassword(string storedString, out string salt)
- {
- if (string.IsNullOrWhiteSpace(storedString)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(storedString));
-
- var saltLen = GenerateSalt();
- salt = storedString.Substring(0, saltLen.Length);
- return storedString.Substring(saltLen.Length);
- }
-
- public static string GenerateSalt()
- {
- var numArray = new byte[16];
- new RNGCryptoServiceProvider().GetBytes(numArray);
- return Convert.ToBase64String(numArray);
- }
-
///
/// Return the hash algorithm to use based on the
///
+ /// The hashing algorithm name.
///
///
- private HashAlgorithm GetCurrentHashAlgorithm()
+ private HashAlgorithm GetHashAlgorithm(string algorithm)
{
- if (_passwordConfiguration.HashAlgorithmType.IsNullOrWhiteSpace())
+ if (algorithm.IsNullOrWhiteSpace())
throw new InvalidOperationException("No hash algorithm type specified");
- var alg = HashAlgorithm.Create(_passwordConfiguration.HashAlgorithmType);
+ var alg = HashAlgorithm.Create(algorithm);
if (alg == null)
- throw new InvalidOperationException($"The hash algorithm specified {_passwordConfiguration.HashAlgorithmType} cannot be resolved");
+ throw new InvalidOperationException($"The hash algorithm specified {algorithm} cannot be resolved");
return alg;
}
public bool SupportHashAlgorithm(string algorithm)
{
- if (algorithm.InvariantEquals(typeof(HMACSHA256).Name))
+ // This is for the v6-v8 hashing algorithm
+ if (algorithm.InvariantEquals(Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName))
+ return true;
+
+ // This is for the <= v4 hashing algorithm
+ if (IsLegacySHA1Algorithm(algorithm))
return true;
- // TODO: Need to add the old old old format in here too which was just HMACSHA1 IIRC but had a custom key implementation as the password itself
return false;
}
+ private bool IsLegacySHA1Algorithm(string algorithm) => algorithm.InvariantEquals(Constants.Security.AspNetUmbraco4PasswordHashAlgorithmName);
+
+ ///
+ /// Hashes the password with the old v4 algorithm
+ ///
+ /// The password.
+ /// The encoded password.
+ private string HashLegacySHA1Password(string password)
+ {
+ var hashAlgorithm = GetLegacySHA1Algorithm(password);
+ var hash = Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(password)));
+ return hash;
+ }
+
+ ///
+ /// Returns the old v4 algorithm and settings
+ ///
+ ///
+ ///
+ private HashAlgorithm GetLegacySHA1Algorithm(string password)
+ {
+ return new HMACSHA1
+ {
+ //the legacy salt was actually the password :(
+ Key = Encoding.Unicode.GetBytes(password)
+ };
+ }
+
}
}
diff --git a/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs
index d14a951877..84fc0e599c 100644
--- a/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs
+++ b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs
@@ -246,6 +246,7 @@ namespace Umbraco.Core.BackOffice
if (string.IsNullOrEmpty(passwordHash)) throw new ArgumentException("Value can't be empty.", nameof(passwordHash));
user.PasswordHash = passwordHash;
+ user.PasswordConfig = null; // Clear this so that it's reset at the repository level
return Task.CompletedTask;
}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/LegacyPasswordSecurityTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/LegacyPasswordSecurityTests.cs
new file mode 100644
index 0000000000..c070657dd5
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/LegacyPasswordSecurityTests.cs
@@ -0,0 +1,109 @@
+using Moq;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Security;
+
+namespace Umbraco.Tests.UnitTests.Umbraco.Core.Security
+{
+ [TestFixture]
+ public class LegacyPasswordSecurityTests
+ {
+
+ [Test]
+ public void Check_Password_Hashed_Non_KeyedHashAlgorithm()
+ {
+ var passwordConfiguration = Mock.Of(x => x.HashAlgorithmType == "SHA256");
+ var passwordSecurity = new LegacyPasswordSecurity();
+
+ string salt;
+ var pass = "ThisIsAHashedPassword";
+ var hashed = passwordSecurity.HashNewPassword(passwordConfiguration.HashAlgorithmType, pass, out salt);
+ var storedPassword = passwordSecurity.FormatPasswordForStorage(passwordConfiguration.HashAlgorithmType, hashed, salt);
+
+ var result = passwordSecurity.VerifyPassword(passwordConfiguration.HashAlgorithmType, "ThisIsAHashedPassword", storedPassword);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public void Check_Password_Hashed_KeyedHashAlgorithm()
+ {
+ var passwordConfiguration = Mock.Of(x => x.HashAlgorithmType == Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName);
+ var passwordSecurity = new LegacyPasswordSecurity();
+
+ string salt;
+ var pass = "ThisIsAHashedPassword";
+ var hashed = passwordSecurity.HashNewPassword(passwordConfiguration.HashAlgorithmType, pass, out salt);
+ var storedPassword = passwordSecurity.FormatPasswordForStorage(passwordConfiguration.HashAlgorithmType, hashed, salt);
+
+ var result = passwordSecurity.VerifyPassword(passwordConfiguration.HashAlgorithmType, "ThisIsAHashedPassword", storedPassword);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public void Check_Password_Legacy_v4_SHA1()
+ {
+ var passwordConfiguration = Mock.Of(x => x.HashAlgorithmType == Constants.Security.AspNetUmbraco4PasswordHashAlgorithmName);
+ var passwordSecurity = new LegacyPasswordSecurity();
+
+ string salt;
+ var pass = "ThisIsAHashedPassword";
+ var hashed = passwordSecurity.HashNewPassword(passwordConfiguration.HashAlgorithmType, pass, out salt);
+ var storedPassword = passwordSecurity.FormatPasswordForStorage(passwordConfiguration.HashAlgorithmType, hashed, salt);
+
+ var result = passwordSecurity.VerifyPassword(passwordConfiguration.HashAlgorithmType, "ThisIsAHashedPassword", storedPassword);
+
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public void Format_Pass_For_Storage_Hashed()
+ {
+ var passwordConfiguration = Mock.Of(x => x.HashAlgorithmType == Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName);
+ var passwordSecurity = new LegacyPasswordSecurity();
+
+ var salt = LegacyPasswordSecurity.GenerateSalt();
+ var stored = "ThisIsAHashedPassword";
+
+ var result = passwordSecurity.FormatPasswordForStorage(passwordConfiguration.HashAlgorithmType, stored, salt);
+
+ Assert.AreEqual(salt + "ThisIsAHashedPassword", result);
+ }
+
+ [Test]
+ public void Get_Stored_Password_Hashed()
+ {
+ var passwordConfiguration = Mock.Of(x => x.HashAlgorithmType == Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName);
+ var passwordSecurity = new LegacyPasswordSecurity();
+
+ var salt = LegacyPasswordSecurity.GenerateSalt();
+ var stored = salt + "ThisIsAHashedPassword";
+
+ string initSalt;
+ var result = passwordSecurity.ParseStoredHashPassword(passwordConfiguration.HashAlgorithmType, stored, out initSalt);
+
+ Assert.AreEqual("ThisIsAHashedPassword", result);
+ }
+
+ ///
+ /// The salt generated is always the same length
+ ///
+ [Test]
+ public void Check_Salt_Length()
+ {
+ var lastLength = 0;
+ for (var i = 0; i < 10000; i++)
+ {
+ var result = LegacyPasswordSecurity.GenerateSalt();
+
+ if (i > 0)
+ Assert.AreEqual(lastLength, result.Length);
+
+ lastLength = result.Length;
+ }
+ }
+
+ }
+}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/PasswordSecurityTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/PasswordSecurityTests.cs
deleted file mode 100644
index 0ef6063910..0000000000
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Security/PasswordSecurityTests.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using Moq;
-using NUnit.Framework;
-using System.Security.Cryptography;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Security;
-
-namespace Umbraco.Tests.UnitTests.Umbraco.Core.Security
-{
- [TestFixture]
- public class PasswordSecurityTests
- {
-
- [Test]
- public void Check_Password_Hashed_Non_KeyedHashAlgorithm()
- {
- var passwordSecurity = new LegacyPasswordSecurity(Mock.Of(x => x.HashAlgorithmType == "SHA256"));
-
- string salt;
- var pass = "ThisIsAHashedPassword";
- var hashed = passwordSecurity.HashNewPassword(pass, out salt);
- var storedPassword = passwordSecurity.FormatPasswordForStorage(hashed, salt);
-
- var result = passwordSecurity.VerifyPassword("ThisIsAHashedPassword", storedPassword);
-
- Assert.IsTrue(result);
- }
-
- [Test]
- public void Check_Password_Hashed_KeyedHashAlgorithm()
- {
- var passwordSecurity = new LegacyPasswordSecurity(Mock.Of(x => x.HashAlgorithmType == Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName));
-
- string salt;
- var pass = "ThisIsAHashedPassword";
- var hashed = passwordSecurity.HashNewPassword(pass, out salt);
- var storedPassword = passwordSecurity.FormatPasswordForStorage(hashed, salt);
-
- var result = passwordSecurity.VerifyPassword("ThisIsAHashedPassword", storedPassword);
-
- Assert.IsTrue(result);
- }
-
- [Test]
- public void Format_Pass_For_Storage_Hashed()
- {
- var passwordSecurity = new LegacyPasswordSecurity(Mock.Of(x => x.HashAlgorithmType == Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName));
-
- var salt = LegacyPasswordSecurity.GenerateSalt();
- var stored = "ThisIsAHashedPassword";
-
- var result = passwordSecurity.FormatPasswordForStorage(stored, salt);
-
- Assert.AreEqual(salt + "ThisIsAHashedPassword", result);
- }
-
- [Test]
- public void Get_Stored_Password_Hashed()
- {
- var passwordSecurity = new LegacyPasswordSecurity(Mock.Of(x => x.HashAlgorithmType == Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName));
-
- var salt = LegacyPasswordSecurity.GenerateSalt();
- var stored = salt + "ThisIsAHashedPassword";
-
- string initSalt;
- var result = passwordSecurity.ParseStoredHashPassword(stored, out initSalt);
-
- Assert.AreEqual("ThisIsAHashedPassword", result);
- }
-
- ///
- /// The salt generated is always the same length
- ///
- [Test]
- public void Check_Salt_Length()
- {
- var lastLength = 0;
- for (var i = 0; i < 10000; i++)
- {
- var result = LegacyPasswordSecurity.GenerateSalt();
-
- if (i > 0)
- Assert.AreEqual(lastLength, result.Length);
-
- lastLength = result.Length;
- }
- }
-
- }
-}
diff --git a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs
deleted file mode 100644
index 1dc261714c..0000000000
--- a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs
+++ /dev/null
@@ -1,165 +0,0 @@
-using System;
-using System.Collections.Specialized;
-using System.Configuration.Provider;
-using System.Security.Cryptography;
-using System.Web.Security;
-using Moq;
-using NUnit.Framework;
-using Umbraco.Core.Security;
-using Umbraco.Tests.TestHelpers;
-using Umbraco.Tests.Testing;
-using Umbraco.Web.Composing;
-using Umbraco.Web.Security;
-
-namespace Umbraco.Tests.Membership
-{
- [TestFixture]
- [UmbracoTest(WithApplication = true)]
- public class MembershipProviderBaseTests : UmbracoTestBase
- {
- [Test]
- public void ChangePasswordQuestionAndAnswer_Without_RequiresQuestionAndAnswer()
- {
- var providerMock = new Mock(TestHelper.GetHostingEnvironment()) { CallBase = true };
- providerMock.Setup(@base => @base.RequiresQuestionAndAnswer).Returns(false);
- var provider = providerMock.Object;
-
- Assert.Throws(() => provider.ChangePasswordQuestionAndAnswer("test", "test", "test", "test"));
- }
-
- [Test]
- public void CreateUser_Not_Whitespace()
- {
- var providerMock = new Mock(TestHelper.GetHostingEnvironment()) {CallBase = true};
- var provider = providerMock.Object;
-
- MembershipCreateStatus status;
- var result = provider.CreateUser("", "", "test@test.com", "", "", true, "", out status);
-
- Assert.IsNull(result);
- Assert.AreEqual(MembershipCreateStatus.InvalidUserName, status);
- }
-
- [Test]
- public void CreateUser_Invalid_Question()
- {
- var providerMock = new Mock(TestHelper.GetHostingEnvironment()) { CallBase = true };
- providerMock.Setup(@base => @base.RequiresQuestionAndAnswer).Returns(true);
- var provider = providerMock.Object;
-
- MembershipCreateStatus status;
- var result = provider.CreateUser("test", "test", "test@test.com", "", "", true, "", out status);
-
- Assert.IsNull(result);
- Assert.AreEqual(MembershipCreateStatus.InvalidQuestion, status);
- }
-
- [Test]
- public void CreateUser_Invalid_Answer()
- {
- var providerMock = new Mock(TestHelper.GetHostingEnvironment()) { CallBase = true };
- providerMock.Setup(@base => @base.RequiresQuestionAndAnswer).Returns(true);
- var provider = providerMock.Object;
-
- MembershipCreateStatus status;
- var result = provider.CreateUser("test", "test", "test@test.com", "test", "", true, "", out status);
-
- Assert.IsNull(result);
- Assert.AreEqual(MembershipCreateStatus.InvalidAnswer, status);
- }
-
- [Test]
- public void GetPassword_Without_EnablePasswordRetrieval()
- {
- var providerMock = new Mock(TestHelper.GetHostingEnvironment()) { CallBase = true };
- providerMock.Setup(@base => @base.EnablePasswordRetrieval).Returns(false);
- var provider = providerMock.Object;
-
- Assert.Throws(() => provider.GetPassword("test", "test"));
- }
-
- [Test]
- public void GetPassword_With_Hashed()
- {
- var providerMock = new Mock(TestHelper.GetHostingEnvironment()) { CallBase = true };
- providerMock.Setup(@base => @base.EnablePasswordRetrieval).Returns(true);
- providerMock.Setup(@base => @base.PasswordFormat).Returns(MembershipPasswordFormat.Hashed);
- var provider = providerMock.Object;
-
- Assert.Throws(() => provider.GetPassword("test", "test"));
- }
-
- // FIXME: in v7 this test relies on ApplicationContext.Current being null, which makes little
- // sense, not going to port the weird code in MembershipProviderBase.ResetPassword, so
- // what shall we do?
- [Test]
- [Ignore("makes no sense?")]
- public void ResetPassword_Without_EnablePasswordReset()
- {
- var providerMock = new Mock(TestHelper.GetHostingEnvironment()) { CallBase = true };
- providerMock.Setup(@base => @base.EnablePasswordReset).Returns(false);
- var provider = providerMock.Object;
-
- Assert.Throws(() => provider.ResetPassword("test", "test"));
- }
-
- [Test]
- public void Sets_Defaults()
- {
- var providerMock = new Mock(TestHelper.GetHostingEnvironment()) { CallBase = true };
- var provider = providerMock.Object;
- provider.Initialize("test", new NameValueCollection());
-
- Assert.AreEqual("test", provider.Name);
- Assert.AreEqual(MembershipProviderBase.GetDefaultAppName(TestHelper.GetHostingEnvironment()), provider.ApplicationName);
- Assert.AreEqual(false, provider.EnablePasswordRetrieval);
- Assert.AreEqual(true, provider.EnablePasswordReset);
- Assert.AreEqual(false, provider.RequiresQuestionAndAnswer);
- Assert.AreEqual(true, provider.RequiresUniqueEmail);
- Assert.AreEqual(5, provider.MaxInvalidPasswordAttempts);
- Assert.AreEqual(10, provider.PasswordAttemptWindow);
- Assert.AreEqual(provider.DefaultMinPasswordLength, provider.MinRequiredPasswordLength);
- Assert.AreEqual(provider.DefaultMinNonAlphanumericChars, provider.MinRequiredNonAlphanumericCharacters);
- Assert.AreEqual(null, provider.PasswordStrengthRegularExpression);
- Assert.AreEqual(provider.DefaultUseLegacyEncoding, provider.UseLegacyEncoding);
- Assert.AreEqual(MembershipPasswordFormat.Hashed, provider.PasswordFormat);
- }
-
- [Test]
- public void Throws_Exception_With_Hashed_Password_And_Password_Retrieval()
- {
- var providerMock = new Mock(TestHelper.GetHostingEnvironment()) { CallBase = true };
- var provider = providerMock.Object;
-
- Assert.Throws(() => provider.Initialize("test", new NameValueCollection()
- {
- {"enablePasswordRetrieval", "true"},
- {"passwordFormat", "Hashed"}
- }));
- }
-
- [TestCase("hello", 0, "", 5, true)]
- [TestCase("hello", 0, "", 4, true)]
- [TestCase("hello", 0, "", 6, false)]
- [TestCase("hello", 1, "", 5, false)]
- [TestCase("hello!", 1, "", 0, true)]
- [TestCase("hello!", 2, "", 0, false)]
- [TestCase("hello!!", 2, "", 0, true)]
- //8 characters or more in length, at least 1 lowercase letter,at least 1 character that is not a lower letter.
- [TestCase("hello", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, false)]
- [TestCase("helloooo", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, false)]
- [TestCase("helloooO", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, true)]
- [TestCase("HELLOOOO", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, false)]
- [TestCase("HELLOOOo", 0, "(?=.{8,})[a-z]+[^a-z]+|[^a-z]+[a-z]+", 0, true)]
- public void Valid_Password(string password, int minRequiredNonAlphanumericChars, string strengthRegex, int minLength, bool pass)
- {
- var result = MembershipProviderBase.IsPasswordValid(password, minRequiredNonAlphanumericChars, strengthRegex, minLength);
- Assert.AreEqual(pass, result.Success);
- }
-
-
-
-
-
- }
-}
diff --git a/src/Umbraco.Tests/Membership/UmbracoServiceMembershipProviderTests.cs b/src/Umbraco.Tests/Membership/UmbracoServiceMembershipProviderTests.cs
deleted file mode 100644
index 4f2cb22b4a..0000000000
--- a/src/Umbraco.Tests/Membership/UmbracoServiceMembershipProviderTests.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-using System.Collections.Specialized;
-using System.Threading;
-using System.Web.Security;
-using Moq;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Models;
-using Umbraco.Core.Services;
-using Umbraco.Tests.TestHelpers;
-using Umbraco.Tests.TestHelpers.Entities;
-using Umbraco.Tests.Testing;
-using Umbraco.Web.Security.Providers;
-
-namespace Umbraco.Tests.Membership
-{
- [TestFixture]
- [Apartment(ApartmentState.STA)]
- [UmbracoTest(Database = UmbracoTestOptions.Database.None, WithApplication = true)]
- public class UmbracoServiceMembershipProviderTests : UmbracoTestBase
- {
- [Test]
- public void Sets_Default_Member_Type_From_Service_On_Init()
- {
- var memberTypeServiceMock = new Mock();
- memberTypeServiceMock.Setup(x => x.GetDefault()).Returns("Blah");
- var provider = new MembersMembershipProvider(Mock.Of(), memberTypeServiceMock.Object, TestHelper.GetUmbracoVersion(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver());
- provider.Initialize("test", new NameValueCollection());
-
- Assert.AreEqual("Blah", provider.DefaultMemberTypeAlias);
- }
-
- [Test]
- public void Sets_Default_Member_Type_From_Config_On_Init()
- {
- var memberTypeServiceMock = new Mock();
- memberTypeServiceMock.Setup(x => x.GetDefault()).Returns("Blah");
- var provider = new MembersMembershipProvider(Mock.Of(), memberTypeServiceMock.Object, TestHelper.GetUmbracoVersion(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver());
- provider.Initialize("test", new NameValueCollection { { "defaultMemberTypeAlias", "Hello" } });
-
- Assert.AreEqual("Hello", provider.DefaultMemberTypeAlias);
- }
-
- [Test]
- public void Create_User_Already_Exists()
- {
- var memberTypeServiceMock = new Mock();
- memberTypeServiceMock.Setup(x => x.GetDefault()).Returns("Member");
- var membershipServiceMock = new Mock();
- membershipServiceMock.Setup(service => service.Exists("test")).Returns(true);
-
- var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, TestHelper.GetUmbracoVersion(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver());
- provider.Initialize("test", new NameValueCollection());
-
- MembershipCreateStatus status;
- var user = provider.CreateUser("test", "test", "testtest$1", "test@test.com", "test", "test", true, "test", out status);
-
- Assert.IsNull(user);
- }
-
- [Test]
- public void Create_User_Requires_Unique_Email()
- {
- var memberTypeServiceMock = new Mock();
- memberTypeServiceMock.Setup(x => x.GetDefault()).Returns("Member");
- var membershipServiceMock = new Mock();
- membershipServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => new Member("test", MockedContentTypes.CreateSimpleMemberType()));
-
- var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, TestHelper.GetUmbracoVersion(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver());
- provider.Initialize("test", new NameValueCollection { { "requiresUniqueEmail", "true" } });
-
- MembershipCreateStatus status;
- var user = provider.CreateUser("test", "test", "testtest$1", "test@test.com", "test", "test", true, "test", out status);
-
- Assert.IsNull(user);
- }
-
- [Test]
- public void Password_Hashed_With_Salt()
- {
- IMember createdMember = null;
- var memberType = MockedContentTypes.CreateSimpleMemberType();
- foreach (var p in ConventionsHelper.GetStandardPropertyTypeStubs(TestHelper.ShortStringHelper))
- {
- memberType.AddPropertyType(p.Value);
- }
- var memberTypeServiceMock = new Mock();
- memberTypeServiceMock.Setup(x => x.GetDefault()).Returns("Member");
- var membershipServiceMock = new Mock();
- membershipServiceMock.Setup(service => service.Exists("test")).Returns(false);
- membershipServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => null);
- membershipServiceMock.Setup(
- service => service.CreateWithIdentity(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
- .Callback((string u, string e, string p, string m, bool isApproved) =>
- {
- createdMember = new Member("test", e, u, p, memberType, isApproved);
- })
- .Returns(() => createdMember);
-
- var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, TestHelper.GetUmbracoVersion(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver());
- provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Hashed" }, { "hashAlgorithmType", Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName } });
-
-
- MembershipCreateStatus status;
- provider.CreateUser("test", "test", "testtest$1", "test@test.com", "test", "test", true, "test", out status);
-
- Assert.AreNotEqual("test", createdMember.RawPasswordValue);
-
- string salt;
- var storedPassword = provider.PasswordSecurity.ParseStoredHashPassword(createdMember.RawPasswordValue, out salt);
- var hashedPassword = provider.PasswordSecurity.HashPassword("testtest$1", salt);
- Assert.AreEqual(hashedPassword, storedPassword);
- }
-
- }
-}
diff --git a/src/Umbraco.Tests/Security/BackOfficeOwinUserManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeOwinUserManagerTests.cs
deleted file mode 100644
index 32c85121d2..0000000000
--- a/src/Umbraco.Tests/Security/BackOfficeOwinUserManagerTests.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Identity;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-using Microsoft.Owin.Security.DataProtection;
-using Moq;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.BackOffice;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Configuration.Models;
-using Umbraco.Core.Models.Membership;
-using Umbraco.Net;
-using Umbraco.Web.Security;
-
-namespace Umbraco.Tests.Security
-{
- public class BackOfficeOwinUserManagerTests
- {
- [Test]
- public async Task CheckPasswordAsync_When_Default_Password_Hasher_Validates_Umbraco7_Hash_Expect_Valid_Password()
- {
- const string v7Hash = "7Uob6fMTTxDIhWGebYiSxg==P+hgvWlXLbDd4cFLADn811KOaVI/9pg1PNvTuG5NklY=";
- const string plaintext = "4XxzH3s3&J";
-
- var userPasswordConfiguration = new UserPasswordConfigurationSettings()
- {
- HashAlgorithmType = Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName
- };
- var mockIpResolver = new Mock();
- var mockUserStore = new Mock>();
- var mockDataProtectionProvider = new Mock();
-
- mockDataProtectionProvider.Setup(x => x.Create(It.IsAny()))
- .Returns(new Mock().Object);
-
-
- var userManager = BackOfficeOwinUserManager.Create(
- Options.Create(userPasswordConfiguration),
- mockIpResolver.Object,
- mockUserStore.Object,
- null,
- mockDataProtectionProvider.Object,
- new NullLogger>());
-
- var globalSettings = new GlobalSettings()
- {
- DefaultUILanguage = "test"
- };
-
- var user = new BackOfficeIdentityUser(globalSettings, 2, new List())
- {
- UserName = "alice",
- Name = "Alice",
- Email = "alice@umbraco.test",
- PasswordHash = v7Hash
- };
-
- mockUserStore.Setup(x => x.GetPasswordHashAsync(user, It.IsAny()))
- .ReturnsAsync(v7Hash);
-
- var isValidPassword = await userManager.CheckPasswordAsync(user, plaintext);
-
- Assert.True(isValidPassword);
- }
- }
-}
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index e8b188bd63..8e7e38c495 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -150,7 +150,6 @@
-
@@ -233,8 +232,6 @@
-
-
diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
index 18393421d2..fd4d5c96cc 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
@@ -305,18 +305,22 @@ namespace Umbraco.Web.BackOffice.Controllers
private IMember CreateMemberData(MemberSave contentItem)
{
- var memberType = _memberTypeService.Get(contentItem.ContentTypeAlias);
- if (memberType == null)
- throw new InvalidOperationException($"No member type found with alias {contentItem.ContentTypeAlias}");
- var member = new Member(contentItem.Name, contentItem.Email, contentItem.Username, memberType, true)
- {
- CreatorId = _backofficeSecurityAccessor.BackofficeSecurity.CurrentUser.Id,
- RawPasswordValue = _passwordSecurity.HashPasswordForStorage(contentItem.Password.NewPassword),
- Comments = contentItem.Comments,
- IsApproved = contentItem.IsApproved
- };
+ throw new NotImplementedException("Members have not been migrated to netcore");
- return member;
+ // TODO: all member password processing and creation needs to be done with a new aspnet identity MemberUserManager that hasn't been created yet.
+
+ //var memberType = _memberTypeService.Get(contentItem.ContentTypeAlias);
+ //if (memberType == null)
+ // throw new InvalidOperationException($"No member type found with alias {contentItem.ContentTypeAlias}");
+ //var member = new Member(contentItem.Name, contentItem.Email, contentItem.Username, memberType, true)
+ //{
+ // CreatorId = _backofficeSecurityAccessor.BackofficeSecurity.CurrentUser.Id,
+ // RawPasswordValue = _passwordSecurity.HashPasswordForStorage(contentItem.Password.NewPassword),
+ // Comments = contentItem.Comments,
+ // IsApproved = contentItem.IsApproved
+ //};
+
+ //return member;
}
///
@@ -372,8 +376,10 @@ namespace Umbraco.Web.BackOffice.Controllers
if (contentItem.Password == null)
return;
+ throw new NotImplementedException("Members have not been migrated to netcore");
+ // TODO: all member password processing and creation needs to be done with a new aspnet identity MemberUserManager that hasn't been created yet.
// set the password
- contentItem.PersistedContent.RawPasswordValue = _passwordSecurity.HashPasswordForStorage(contentItem.Password.NewPassword);
+ //contentItem.PersistedContent.RawPasswordValue = _passwordSecurity.HashPasswordForStorage(contentItem.Password.NewPassword);
}
private static void UpdateName(MemberSave memberSave)
diff --git a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs
index 8dc1b8c417..71fa97b9cb 100644
--- a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs
+++ b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs
@@ -86,7 +86,7 @@ namespace Umbraco.Extensions
services.TryAddScoped, PasswordValidator>();
services.TryAddScoped>(
services => new BackOfficePasswordHasher(
- new LegacyPasswordSecurity(services.GetRequiredService>().Value),
+ new LegacyPasswordSecurity(),
services.GetRequiredService()));
services.TryAddScoped, DefaultUserConfirmation>();
services.TryAddScoped, UserClaimsPrincipalFactory>();
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficePasswordHasher.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficePasswordHasher.cs
index df3bc2935b..6c61e7bb35 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficePasswordHasher.cs
+++ b/src/Umbraco.Web.BackOffice/Security/BackOfficePasswordHasher.cs
@@ -11,72 +11,73 @@ namespace Umbraco.Web.BackOffice.Security
///
/// A password hasher for back office users
///
+ ///
+ /// This allows us to verify passwords in old formats and roll forward to the latest format
+ ///
public class BackOfficePasswordHasher : PasswordHasher
{
- private readonly LegacyPasswordSecurity _passwordSecurity;
+ private readonly LegacyPasswordSecurity _legacyPasswordSecurity;
private readonly IJsonSerializer _jsonSerializer;
+ private readonly PasswordHasher _aspnetV2PasswordHasher = new PasswordHasher(new V2PasswordHasherOptions());
public BackOfficePasswordHasher(LegacyPasswordSecurity passwordSecurity, IJsonSerializer jsonSerializer)
{
- _passwordSecurity = passwordSecurity;
+ _legacyPasswordSecurity = passwordSecurity;
_jsonSerializer = jsonSerializer;
}
public override string HashPassword(BackOfficeIdentityUser user, string password)
+ {
+ // Always use the latest/current hash algorithm when hashing new passwords for storage.
+ // NOTE: This is only overridden to show that we can since we may need to adjust this in the future
+ // if new/different formats are required.
+ return base.HashPassword(user, password);
+ }
+
+ ///
+ /// Verifies a user's hashed password
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// This will check the user's current hashed password format stored with their user row and use that to verify the hash. This could be any hashes
+ /// from the very old v4, to the older v6-v8, to the older aspnet identity and finally to the most recent
+ ///
+ public override PasswordVerificationResult VerifyHashedPassword(BackOfficeIdentityUser user, string hashedPassword, string providedPassword)
{
if (!user.PasswordConfig.IsNullOrWhiteSpace())
{
// check if the (legacy) password security supports this hash algorith and if so then use it
var deserialized = _jsonSerializer.Deserialize(user.PasswordConfig);
- if (_passwordSecurity.SupportHashAlgorithm(deserialized.HashAlgorithm))
- return _passwordSecurity.HashPasswordForStorage(password);
+ if (_legacyPasswordSecurity.SupportHashAlgorithm(deserialized.HashAlgorithm))
+ {
+ var result = _legacyPasswordSecurity.VerifyPassword(deserialized.HashAlgorithm, providedPassword, hashedPassword);
+ return result
+ ? PasswordVerificationResult.SuccessRehashNeeded
+ : PasswordVerificationResult.Failed;
+ }
- // We will explicitly detect names here since this allows us to future proof these checks.
+ // We will explicitly detect names here
// The default is PBKDF2.ASPNETCORE.V3:
// PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
// The underlying class only lets us change 2 things which is the version: options.CompatibilityMode and the iteration count
// The PBKDF2.ASPNETCORE.V2 settings are:
// PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations.
- switch (deserialized.HashAlgorithm)
- {
- case Constants.Security.AspNetCoreV3PasswordHashAlgorithmName:
- return base.HashPassword(user, password);
- case Constants.Security.AspNetCoreV2PasswordHashAlgorithmName:
- var v2Hasher = new PasswordHasher(new V2PasswordHasherOptions());
- return v2Hasher.HashPassword(user, password);
- }
- }
-
- // else keep the default
- return base.HashPassword(user, password);
- }
-
- public override PasswordVerificationResult VerifyHashedPassword(BackOfficeIdentityUser user, string hashedPassword, string providedPassword)
- {
- if (!user.PasswordConfig.IsNullOrWhiteSpace())
- {
- // check if the (legacy) password security supports this hash algorith and if so then use it
- var deserialized = _jsonSerializer.Deserialize(user.PasswordConfig);
- if (_passwordSecurity.SupportHashAlgorithm(deserialized.HashAlgorithm))
- {
- var result = _passwordSecurity.VerifyPassword(providedPassword, hashedPassword);
- return result
- ? PasswordVerificationResult.Success
- : PasswordVerificationResult.Failed;
- }
-
switch (deserialized.HashAlgorithm)
{
case Constants.Security.AspNetCoreV3PasswordHashAlgorithmName:
return base.VerifyHashedPassword(user, hashedPassword, providedPassword);
case Constants.Security.AspNetCoreV2PasswordHashAlgorithmName:
- var v2Hasher = new PasswordHasher(new V2PasswordHasherOptions());
- return v2Hasher.VerifyHashedPassword(user, hashedPassword, providedPassword);
+ var legacyResult = _aspnetV2PasswordHasher.VerifyHashedPassword(user, hashedPassword, providedPassword);
+ if (legacyResult == PasswordVerificationResult.Success) return PasswordVerificationResult.SuccessRehashNeeded;
+ return legacyResult;
}
}
- // else go the default
+ // else go the default (v3)
return base.VerifyHashedPassword(user, hashedPassword, providedPassword);
}
diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs
index 45411b5e69..0d91892445 100644
--- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs
+++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs
@@ -39,7 +39,7 @@ namespace Umbraco.Web.Common.Runtime
[ComposeAfter(typeof(CoreInitialComposer))]
public class AspNetCoreComposer : ComponentComposer, IComposer
{
- public new void Compose(Composition composition)
+ public override void Compose(Composition composition)
{
base.Compose(composition);
@@ -99,7 +99,7 @@ namespace Umbraco.Web.Common.Runtime
composition.RegisterUnique();
composition.RegisterUnique();
- composition.RegisterUnique(factory => new LegacyPasswordSecurity(factory.GetInstance>().Value));
+ composition.RegisterUnique(factory => new LegacyPasswordSecurity());
}
}
}
diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js b/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js
index bf5fe6b4fb..179faeb843 100644
--- a/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js
+++ b/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js
@@ -281,7 +281,7 @@ function dependencies() {
var assetsTask = gulp.src(config.sources.globs.assets, { allowEmpty: true });
assetsTask = assetsTask.pipe(imagemin([
imagemin.gifsicle({interlaced: true}),
- imagemin.jpegtran({progressive: true}),
+ imagemin.mozjpeg({progressive: true}),
imagemin.optipng({optimizationLevel: 5}),
imagemin.svgo({
plugins: [
diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json
index 5c4df7b12c..eae04dc289 100644
--- a/src/Umbraco.Web.UI.Client/package.json
+++ b/src/Umbraco.Web.UI.Client/package.json
@@ -61,7 +61,7 @@
"gulp-cli": "^2.3.0",
"gulp-concat": "2.6.1",
"gulp-eslint": "6.0.0",
- "gulp-imagemin": "6.1.1",
+ "gulp-imagemin": "7.1.0",
"gulp-less": "4.0.1",
"gulp-notify": "^3.0.0",
"gulp-postcss": "8.0.0",
diff --git a/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs b/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs
index 65d7836d42..2f5e858687 100644
--- a/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs
+++ b/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs
@@ -7,15 +7,14 @@ using Microsoft.Extensions.Options;
using Microsoft.Owin.Security.DataProtection;
using Umbraco.Core;
using Umbraco.Core.BackOffice;
-using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Mapping;
-using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Net;
namespace Umbraco.Web.Security
{
+ // TODO: Most of this is already migrated to netcore, there's probably not much more to go and then we can complete remove it
public class BackOfficeOwinUserManager : BackOfficeUserManager
{
public const string OwinMarkerKey = "Umbraco.Web.Security.Identity.BackOfficeUserManagerMarker";
@@ -118,11 +117,6 @@ namespace Umbraco.Web.Security
#endregion
- protected override IPasswordHasher GetDefaultPasswordHasher(IPasswordConfiguration passwordConfiguration)
- {
- return new UserAwarePasswordHasher(new LegacyPasswordSecurity(passwordConfiguration));
- }
-
protected void InitUserManager(BackOfficeOwinUserManager manager, IDataProtectionProvider dataProtectionProvider)
{
// use a custom hasher based on our membership provider
diff --git a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs
index 10adc76e6b..fa1ddae980 100644
--- a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs
+++ b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs
@@ -80,7 +80,7 @@ namespace Umbraco.Web.Security.Providers
CustomHashAlgorithmType.IsNullOrWhiteSpace() ? Membership.HashAlgorithmType : CustomHashAlgorithmType,
MaxInvalidPasswordAttempts));
- _passwordSecurity = new Lazy(() => new LegacyPasswordSecurity(PasswordConfiguration));
+ _passwordSecurity = new Lazy(() => new LegacyPasswordSecurity());
}
diff --git a/src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs
index 53cbd266dc..8a92226d7e 100644
--- a/src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs
+++ b/src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs
@@ -83,9 +83,9 @@ namespace Umbraco.Web.Security.Providers
if (m == null) return false;
string salt;
- var encodedPassword = PasswordSecurity.HashNewPassword(newPassword, out salt);
+ var encodedPassword = PasswordSecurity.HashNewPassword(Membership.HashAlgorithmType, newPassword, out salt);
- m.RawPasswordValue = PasswordSecurity.FormatPasswordForStorage(encodedPassword, salt);
+ m.RawPasswordValue = PasswordSecurity.FormatPasswordForStorage(Membership.HashAlgorithmType, encodedPassword, salt);
m.LastPasswordChangeDate = DateTime.Now;
MemberService.Save(m);
@@ -143,12 +143,12 @@ namespace Umbraco.Web.Security.Providers
}
string salt;
- var encodedPassword = PasswordSecurity.HashNewPassword(password, out salt);
+ var encodedPassword = PasswordSecurity.HashNewPassword(Membership.HashAlgorithmType, password, out salt);
var member = MemberService.CreateWithIdentity(
username,
email,
- PasswordSecurity.FormatPasswordForStorage(encodedPassword, salt),
+ PasswordSecurity.FormatPasswordForStorage(Membership.HashAlgorithmType, encodedPassword, salt),
memberTypeAlias,
isApproved);
@@ -406,8 +406,8 @@ namespace Umbraco.Web.Security.Providers
}
string salt;
- var encodedPassword = PasswordSecurity.HashNewPassword(generatedPassword, out salt);
- m.RawPasswordValue = PasswordSecurity.FormatPasswordForStorage(encodedPassword, salt);
+ var encodedPassword = PasswordSecurity.HashNewPassword(Membership.HashAlgorithmType, generatedPassword, out salt);
+ m.RawPasswordValue = PasswordSecurity.FormatPasswordForStorage(Membership.HashAlgorithmType, encodedPassword, salt);
m.LastPasswordChangeDate = DateTime.Now;
MemberService.Save(m);
@@ -519,7 +519,7 @@ namespace Umbraco.Web.Security.Providers
};
}
- var authenticated = PasswordSecurity.VerifyPassword(password, member.RawPasswordValue);
+ var authenticated = PasswordSecurity.VerifyPassword(Membership.HashAlgorithmType, password, member.RawPasswordValue);
var requiresFullSave = false;
diff --git a/src/Umbraco.Web/Security/UserAwarePasswordHasher.cs b/src/Umbraco.Web/Security/UserAwarePasswordHasher.cs
deleted file mode 100644
index 040d79be02..0000000000
--- a/src/Umbraco.Web/Security/UserAwarePasswordHasher.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using Microsoft.AspNetCore.Identity;
-using Umbraco.Core.BackOffice;
-using Umbraco.Core.Security;
-
-namespace Umbraco.Web.Security
-{
- public class UserAwarePasswordHasher : IPasswordHasher
- where T : BackOfficeIdentityUser
- {
- private readonly LegacyPasswordSecurity _passwordSecurity;
-
- public UserAwarePasswordHasher(LegacyPasswordSecurity passwordSecurity)
- {
- _passwordSecurity = passwordSecurity;
- }
-
- public string HashPassword(string password)
- {
- return _passwordSecurity.HashPasswordForStorage(password);
- }
-
- public string HashPassword(T user, string password)
- {
- // TODO: Implement the logic for this, we need to lookup the password format for the user and hash accordingly: http://issues.umbraco.org/issue/U4-10089
- //NOTE: For now this just falls back to the hashing we are currently using
-
- return HashPassword(password);
- }
-
- public PasswordVerificationResult VerifyHashedPassword(T user, string hashedPassword, string providedPassword)
- {
- // TODO: Implement the logic for this, we need to lookup the password format for the user and hash accordingly: http://issues.umbraco.org/issue/U4-10089
- //NOTE: For now this just falls back to the hashing we are currently using
-
- return _passwordSecurity.VerifyPassword(providedPassword, hashedPassword)
- ? PasswordVerificationResult.Success
- : PasswordVerificationResult.Failed;
- }
- }
-}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 099c995126..e879f37eef 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -194,7 +194,6 @@
-