diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 36dc181580..26646d2a4f 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -292,6 +292,7 @@ namespace Umbraco.Cms.Core.DependencyInjection Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); + Services.AddUnique(); Services.AddUnique(factory => new ExternalLoginService( factory.GetRequiredService(), factory.GetRequiredService(), diff --git a/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs b/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs index ca3045a4de..35528a48ca 100644 --- a/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs +++ b/src/Umbraco.Core/Security/LegacyPasswordSecurity.cs @@ -209,11 +209,21 @@ namespace Umbraco.Cms.Core.Security { // This is for the v6-v8 hashing algorithm if (algorithm.InvariantEquals(Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName)) + { return true; + } + + // Default validation value for old machine keys (switched to HMACSHA256 aspnet 4 https://docs.microsoft.com/en-us/aspnet/whitepapers/aspnet4/breaking-changes) + if (algorithm.InvariantEquals("SHA1")) + { + return true; + } // This is for the <= v4 hashing algorithm if (IsLegacySHA1Algorithm(algorithm)) + { return true; + } return false; } diff --git a/src/Umbraco.Infrastructure/Services/Implement/TrackedReferencesService.cs b/src/Umbraco.Core/Services/TrackedReferencesService.cs similarity index 98% rename from src/Umbraco.Infrastructure/Services/Implement/TrackedReferencesService.cs rename to src/Umbraco.Core/Services/TrackedReferencesService.cs index 9a4cc8860e..c43d2ca57b 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/TrackedReferencesService.cs +++ b/src/Umbraco.Core/Services/TrackedReferencesService.cs @@ -3,7 +3,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -namespace Umbraco.Cms.Core.Services.Implement +namespace Umbraco.Cms.Core.Services { public class TrackedReferencesService : ITrackedReferencesService { diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs index 5687fb467e..7c72b1ddce 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs @@ -42,12 +42,6 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddTransient(SourcesFactory); builder.Services.AddUnique(factory => CreatePackageRepository(factory, "createdPackages.config")); diff --git a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs index 5c59b5683e..4fb471cbd9 100644 --- a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs +++ b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs @@ -5,13 +5,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Data.SqlClient; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NPoco; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Infrastructure.Migrations.Install; using Umbraco.Cms.Infrastructure.Persistence; @@ -19,7 +16,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Mappers; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Extensions; -using MapperCollection = NPoco.MapperCollection; +using MapperCollection = Umbraco.Cms.Infrastructure.Persistence.Mappers.MapperCollection; namespace Umbraco.Cms.Infrastructure.Runtime { diff --git a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs index a6b0670998..70fb864482 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs @@ -74,28 +74,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _dashboardSettings = dashboardSettings.Value; } - - [Obsolete("Use the constructor that accepts ISiteIdentifierService")] - public DashboardController( - IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - AppCaches appCaches, - ILogger logger, - IDashboardService dashboardService, - IUmbracoVersion umbracoVersion, - IShortStringHelper shortStringHelper, - IOptions dashboardSettings) - : this( - backOfficeSecurityAccessor, - appCaches, - logger, - dashboardService, - umbracoVersion, - shortStringHelper, - dashboardSettings, - StaticServiceProvider.Instance.GetRequiredService()) - { - } - //we have just one instance of HttpClient shared for the entire application private static readonly HttpClient HttpClient = new HttpClient(); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/ScheduledPublishingTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/ScheduledPublishingTests.cs index 08999affe2..1379e99b99 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/ScheduledPublishingTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/ScheduledPublishingTests.cs @@ -113,7 +113,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.HostedServices var mockScopeProvider = new Mock(); mockScopeProvider - .Setup(x => x.CreateScope(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.CreateScope(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(Mock.Of()); return new ScheduledPublishing( diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/UmbracoPasswordHasherTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/UmbracoPasswordHasherTests.cs new file mode 100644 index 0000000000..aa6bb4156b --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/UmbracoPasswordHasherTests.cs @@ -0,0 +1,81 @@ +using AutoFixture.NUnit3; +using Microsoft.AspNetCore.Identity; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Tests.UnitTests.AutoFixture; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security +{ + [TestFixture] + public class UmbracoPasswordHasherTests + { + // Technically MD5, HMACSHA384 & HMACSHA512 were also possible but opt in as opposed to historic defaults. + [Test] + [InlineAutoMoqData("HMACSHA256", "Umbraco9Rocks!", "uB/pLEhhe1W7EtWMv/pSgg==1y8+aso9+h3AKRtJXlVYeg2TZKJUr64hccj82ZZ7Ksk=")] // Actually HMACSHA256 + [InlineAutoMoqData("HMACSHA256", "Umbraco9Rocks!", "t0U8atXTX/efNCtTafukwZeIpr8=")] // v4 site legacy password, with incorrect algorithm specified in database actually HMACSHA1 with password used as key. + [InlineAutoMoqData("SHA1", "Umbraco9Rocks!", "6tZGfG9NTxJJYp19Fac9og==zzRggqANxhb+CbD/VabEt8cIde8=")] // When SHA1 is set on machine key. + public void VerifyHashedPassword_WithValidLegacyPasswordHash_ReturnsSuccessRehashNeeded( + string algorithm, + string providedPassword, + string hashedPassword, + [Frozen] IJsonSerializer jsonSerializer, + TestUserStub aUser, + UmbracoPasswordHasher sut) + { + Mock.Get(jsonSerializer) + .Setup(x => x.Deserialize(It.IsAny())) + .Returns(new PersistedPasswordSettings{ HashAlgorithm = algorithm }); + + var result = sut.VerifyHashedPassword(aUser, hashedPassword, providedPassword); + + Assert.AreEqual(PasswordVerificationResult.SuccessRehashNeeded, result); + } + + + [Test] + [InlineAutoMoqData("PBKDF2.ASPNETCORE.V3", "Umbraco9Rocks!", "AQAAAAEAACcQAAAAEDCrYcnIhHKr38yuchsDu6AFqqmLNvRooKObV25GC1LC1tLY+gWGU4xNug0lc17PHA==")] + public void VerifyHashedPassword_WithValidModernPasswordHash_ReturnsSuccess( + string algorithm, + string providedPassword, + string hashedPassword, + [Frozen] IJsonSerializer jsonSerializer, + TestUserStub aUser, + UmbracoPasswordHasher sut) + { + Mock.Get(jsonSerializer) + .Setup(x => x.Deserialize(It.IsAny())) + .Returns(new PersistedPasswordSettings { HashAlgorithm = algorithm }); + + var result = sut.VerifyHashedPassword(aUser, hashedPassword, providedPassword); + + Assert.AreEqual(PasswordVerificationResult.Success, result); + } + + [Test] + [InlineAutoMoqData("HMACSHA256", "Umbraco9Rocks!", "aB/cDeFaBcDefAbcD/EfaB==1y8+aso9+h3AKRtJXlVYeg2TZKJUr64hccj82ZZ7Ksk=")] + public void VerifyHashedPassword_WithIncorrectPassword_ReturnsFailed( + string algorithm, + string providedPassword, + string hashedPassword, + [Frozen] IJsonSerializer jsonSerializer, + TestUserStub aUser, + UmbracoPasswordHasher sut) + { + Mock.Get(jsonSerializer) + .Setup(x => x.Deserialize(It.IsAny())) + .Returns(new PersistedPasswordSettings { HashAlgorithm = algorithm }); + + var result = sut.VerifyHashedPassword(aUser, hashedPassword, providedPassword); + + Assert.AreEqual(PasswordVerificationResult.Failed, result); + } + + public class TestUserStub : UmbracoIdentityUser + { + public TestUserStub() => PasswordConfig = "not null or empty"; + } + } +}