From c92c633edbb30f956a54156c6510fb2aea038df9 Mon Sep 17 00:00:00 2001 From: Scott Brady Date: Mon, 18 May 2020 08:21:34 +0100 Subject: [PATCH] Reduced duplicated code --- .../Models/Identity}/IdentityUser.cs | 3 +- .../BackOfficeClaimsPrincipalFactory.cs | 5 +- .../BackOffice/BackOfficeIdentityUser.cs | 8 +- .../BackOffice/UserLoginInfoWrapper.cs | 2 +- .../NopLookupNormalizerTests.cs | 2 +- ...kOfficeServiceCollectionExtensionsTests.cs | 16 +- .../BackOfficeClaimsPrincipalFactoryTests.cs | 11 +- .../BackOffice/NopLookupNormalizerTests.cs | 56 +++ .../UmbracoBackOfficeIdentityTests.cs | 9 +- .../Security/BackOfficeUserManagerTests.cs | 4 +- .../OwinDataProtectorTokenProviderTests.cs | 2 +- .../UmbracoSecurityStampValidatorTests.cs | 33 +- .../AuthenticateEverythingMiddleware.cs | 2 +- .../TestControllerActivatorBase.cs | 1 + src/Umbraco.Tests/Umbraco.Tests.csproj | 3 - .../Web/Controllers/UsersControllerTests.cs | 3 +- .../Controllers/BackOfficeController.cs | 1 - ...coBackOfficeServiceCollectionExtensions.cs | 21 +- .../Compose/AuditEventsComponent.cs | 3 +- .../WebMappingProfiles.cs | 2 +- .../Editors/AuthenticationController.cs | 9 +- .../Editors/BackOfficeController.cs | 2 +- src/Umbraco.Web/Editors/PasswordChanger.cs | 6 +- src/Umbraco.Web/Editors/UsersController.cs | 2 +- .../Models/Identity/BackOfficeIdentityUser.cs | 434 ------------------ .../Models/Identity/IdentityMapDefinition.cs | 82 ---- .../Models/Identity/IdentityUser.cs | 116 ----- .../Models/Identity/UserLoginInfoWrapper.cs | 29 -- src/Umbraco.Web/OwinExtensions.cs | 2 +- ...eDirectoryBackOfficeUserPasswordChecker.cs | 2 +- .../Security/AppBuilderExtensions.cs | 2 +- .../Security/AuthenticationExtensions.cs | 35 +- .../BackOfficeCookieAuthenticationProvider.cs | 8 +- .../Security/BackOfficeSignInManager.cs | 4 +- .../Security/BackOfficeUserManager.cs | 2 +- .../Security/BackOfficeUserManagerMarker.cs | 2 +- .../Security/BackOfficeUserStore.cs | 3 +- .../Security/BackOfficeUserValidator.cs | 2 +- .../Security/ExternalSignInAutoLinkOptions.cs | 2 +- .../Security/FixWindowsAuthMiddlware.cs | 2 +- .../Security/IBackOfficeUserManagerMarker.cs | 2 +- .../IBackOfficeUserPasswordChecker.cs | 3 +- src/Umbraco.Web/Security/IUserSessionStore.cs | 15 - .../Security/IdentityAuditEventArgs.cs | 1 + .../Security/NopLookupNormalizer.cs | 14 - .../OwinDataProtectorTokenProvider.cs | 2 +- .../PreviewAuthenticationMiddleware.cs | 4 +- .../Security/UmbracoBackOfficeIdentity.cs | 232 ---------- .../Security/UmbracoSecureDataFormat.cs | 2 +- .../Security/UmbracoSecurityStampValidator.cs | 2 +- .../Security/UserAwarePasswordHasher.cs | 2 +- src/Umbraco.Web/Security/WebSecurity.cs | 3 +- src/Umbraco.Web/Umbraco.Web.csproj | 8 - .../CheckIfUserTicketDataIsStaleAttribute.cs | 3 +- .../WebApi/UmbracoAuthorizedApiController.cs | 8 +- 55 files changed, 158 insertions(+), 1076 deletions(-) rename src/{Umbraco.Infrastructure/BackOffice => Umbraco.Core/Models/Identity}/IdentityUser.cs (98%) rename src/{Umbraco.Web/Security => Umbraco.Infrastructure/BackOffice}/BackOfficeClaimsPrincipalFactory.cs (95%) rename src/{Umbraco.Tests/Security => Umbraco.Tests.Integration/Umbraco.Web.BackOffice}/NopLookupNormalizerTests.cs (97%) rename src/{Umbraco.Tests/Security => Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice}/BackOfficeClaimsPrincipalFactoryTests.cs (95%) create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/NopLookupNormalizerTests.cs rename src/{Umbraco.Tests/Security => Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice}/UmbracoBackOfficeIdentityTests.cs (97%) delete mode 100644 src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs delete mode 100644 src/Umbraco.Web/Models/Identity/IdentityMapDefinition.cs delete mode 100644 src/Umbraco.Web/Models/Identity/IdentityUser.cs delete mode 100644 src/Umbraco.Web/Models/Identity/UserLoginInfoWrapper.cs delete mode 100644 src/Umbraco.Web/Security/IUserSessionStore.cs delete mode 100644 src/Umbraco.Web/Security/NopLookupNormalizer.cs delete mode 100644 src/Umbraco.Web/Security/UmbracoBackOfficeIdentity.cs diff --git a/src/Umbraco.Infrastructure/BackOffice/IdentityUser.cs b/src/Umbraco.Core/Models/Identity/IdentityUser.cs similarity index 98% rename from src/Umbraco.Infrastructure/BackOffice/IdentityUser.cs rename to src/Umbraco.Core/Models/Identity/IdentityUser.cs index 9de30360ae..093e42c1e7 100644 --- a/src/Umbraco.Infrastructure/BackOffice/IdentityUser.cs +++ b/src/Umbraco.Core/Models/Identity/IdentityUser.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; -using Umbraco.Core.Models.Identity; -namespace Umbraco.Core.BackOffice +namespace Umbraco.Core.Models.Identity { /// /// Default IUser implementation diff --git a/src/Umbraco.Web/Security/BackOfficeClaimsPrincipalFactory.cs b/src/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactory.cs similarity index 95% rename from src/Umbraco.Web/Security/BackOfficeClaimsPrincipalFactory.cs rename to src/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactory.cs index fe22981831..a463a84d4b 100644 --- a/src/Umbraco.Web/Security/BackOfficeClaimsPrincipalFactory.cs +++ b/src/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactory.cs @@ -4,10 +4,9 @@ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; -using Umbraco.Core.Security; -using Umbraco.Web.Models.Identity; +using Umbraco.Core.BackOffice; -namespace Umbraco.Web.Security +namespace Umbraco.Core.BackOffice { public class BackOfficeClaimsPrincipalFactory : UserClaimsPrincipalFactory where TUser : BackOfficeIdentityUser diff --git a/src/Umbraco.Infrastructure/BackOffice/BackOfficeIdentityUser.cs b/src/Umbraco.Infrastructure/BackOffice/BackOfficeIdentityUser.cs index 4eca7a5e57..ea160ef1cf 100644 --- a/src/Umbraco.Infrastructure/BackOffice/BackOfficeIdentityUser.cs +++ b/src/Umbraco.Infrastructure/BackOffice/BackOfficeIdentityUser.cs @@ -89,8 +89,8 @@ namespace Umbraco.Core.BackOffice /// public bool HasIdentity => _hasIdentity; - public int[] CalculatedMediaStartNodeIds { get; internal set; } - public int[] CalculatedContentStartNodeIds { get; internal set; } + public int[] CalculatedMediaStartNodeIds { get; set; } + public int[] CalculatedContentStartNodeIds { get; set; } public override int Id { @@ -257,7 +257,7 @@ namespace Umbraco.Core.BackOffice /// /// Based on the user's lockout end date, this will determine if they are locked out /// - internal bool IsLockedOut + public bool IsLockedOut { get { @@ -269,7 +269,7 @@ namespace Umbraco.Core.BackOffice /// /// This is a 1:1 mapping with IUser.IsApproved /// - internal bool IsApproved { get; set; } + public bool IsApproved { get; set; } /// /// Overridden to make the retrieval lazy diff --git a/src/Umbraco.Infrastructure/BackOffice/UserLoginInfoWrapper.cs b/src/Umbraco.Infrastructure/BackOffice/UserLoginInfoWrapper.cs index ab6af35519..a441d0299a 100644 --- a/src/Umbraco.Infrastructure/BackOffice/UserLoginInfoWrapper.cs +++ b/src/Umbraco.Infrastructure/BackOffice/UserLoginInfoWrapper.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Models.Identity; namespace Umbraco.Core.BackOffice { - internal class UserLoginInfoWrapper : IUserLoginInfo + public class UserLoginInfoWrapper : IUserLoginInfo { private readonly UserLoginInfo _info; diff --git a/src/Umbraco.Tests/Security/NopLookupNormalizerTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/NopLookupNormalizerTests.cs similarity index 97% rename from src/Umbraco.Tests/Security/NopLookupNormalizerTests.cs rename to src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/NopLookupNormalizerTests.cs index 2abecbb4dd..1c4e08a4de 100644 --- a/src/Umbraco.Tests/Security/NopLookupNormalizerTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/NopLookupNormalizerTests.cs @@ -1,6 +1,6 @@ using System; using NUnit.Framework; -using Umbraco.Web.Security; +using Umbraco.Core.BackOffice; namespace Umbraco.Tests.Security { diff --git a/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UmbracoBackOfficeServiceCollectionExtensionsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UmbracoBackOfficeServiceCollectionExtensionsTests.cs index 3b61f31b66..51fce283f8 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UmbracoBackOfficeServiceCollectionExtensionsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UmbracoBackOfficeServiceCollectionExtensionsTests.cs @@ -1,16 +1,9 @@ using System; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Moq; using NUnit.Framework; using Umbraco.Extensions; using Umbraco.Core.BackOffice; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Tests.Integration.Extensions; -using Umbraco.Tests.Integration.Implementations; using Umbraco.Tests.Integration.Testing; namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Extensions @@ -27,6 +20,15 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Extensions Assert.AreEqual(typeof(BackOfficeUserStore), userStore.GetType()); } + [Test] + public void AddUmbracoBackOfficeIdentity_ExpectBackOfficeClaimsPrincipalFactoryResolvable() + { + var principalFactory = Services.GetService>(); + + Assert.IsNotNull(principalFactory); + Assert.AreEqual(typeof(BackOfficeClaimsPrincipalFactory), principalFactory.GetType()); + } + [Test] public void AddUmbracoBackOfficeIdentity_ExpectBackOfficeUserManagerResolvable() { diff --git a/src/Umbraco.Tests/Security/BackOfficeClaimsPrincipalFactoryTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs similarity index 95% rename from src/Umbraco.Tests/Security/BackOfficeClaimsPrincipalFactoryTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs index b7b516318c..3c01f89554 100644 --- a/src/Umbraco.Tests/Security/BackOfficeClaimsPrincipalFactoryTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs @@ -7,13 +7,12 @@ using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Security; -using Umbraco.Web.Models.Identity; -using Umbraco.Web.Security; +using Umbraco.Extensions; -namespace Umbraco.Tests.Security +namespace Umbraco.Tests.UnitTests.Umbraco.Core.BackOffice { [TestFixture] public class BackOfficeClaimsPrincipalFactoryTests @@ -131,7 +130,7 @@ namespace Umbraco.Tests.Security const string expectedClaimType = ClaimTypes.Role; const string expectedClaimValue = "b87309fb-4caf-48dc-b45a-2b752d051508"; - _testUser.Roles.Add(new Core.Models.Identity.IdentityUserRole{RoleId = expectedClaimValue}); + _testUser.Roles.Add(new global::Umbraco.Core.Models.Identity.IdentityUserRole{RoleId = expectedClaimValue}); _mockUserManager.Setup(x => x.SupportsUserRole).Returns(true); _mockUserManager.Setup(x => x.GetRolesAsync(_testUser)).ReturnsAsync(new[] {expectedClaimValue}); @@ -148,7 +147,7 @@ namespace Umbraco.Tests.Security const string expectedClaimType = "custom"; const string expectedClaimValue = "val"; - _testUser.Claims.Add(new Core.Models.Identity.IdentityUserClaim {ClaimType = expectedClaimType, ClaimValue = expectedClaimValue}); + _testUser.Claims.Add(new global::Umbraco.Core.Models.Identity.IdentityUserClaim {ClaimType = expectedClaimType, ClaimValue = expectedClaimValue}); _mockUserManager.Setup(x => x.SupportsUserClaim).Returns(true); _mockUserManager.Setup(x => x.GetClaimsAsync(_testUser)).ReturnsAsync( new List {new Claim(expectedClaimType, expectedClaimValue)}); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/NopLookupNormalizerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/NopLookupNormalizerTests.cs new file mode 100644 index 0000000000..e492b060b5 --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/NopLookupNormalizerTests.cs @@ -0,0 +1,56 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.BackOffice; + +namespace Umbraco.Tests.UnitTests.Umbraco.Core.BackOffice +{ + public class NopLookupNormalizerTests + { + [Test] + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + public void NormalizeName_When_Name_Null_Or_Whitespace_Expect_Same_Returned(string name) + { + var sut = new NopLookupNormalizer(); + + var normalizedName = sut.NormalizeName(name); + + Assert.AreEqual(name, normalizedName); + } + + [Test] + public void NormalizeName_Expect_Input_Returned() + { + var name = Guid.NewGuid().ToString(); + var sut = new NopLookupNormalizer(); + + var normalizedName = sut.NormalizeName(name); + + Assert.AreEqual(name, normalizedName); + } + [Test] + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + public void NormalizeEmail_When_Name_Null_Or_Whitespace_Expect_Same_Returned(string email) + { + var sut = new NopLookupNormalizer(); + + var normalizedEmail = sut.NormalizeEmail(email); + + Assert.AreEqual(email, normalizedEmail); + } + + [Test] + public void NormalizeEmail_Expect_Input_Returned() + { + var email = $"{Guid.NewGuid()}@umbraco"; + var sut = new NopLookupNormalizer(); + + var normalizedEmail = sut.NormalizeEmail(email); + + Assert.AreEqual(email, normalizedEmail); + } + } +} diff --git a/src/Umbraco.Tests/Security/UmbracoBackOfficeIdentityTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs similarity index 97% rename from src/Umbraco.Tests/Security/UmbracoBackOfficeIdentityTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs index 9c16d0c35a..5d0cec0e6e 100644 --- a/src/Umbraco.Tests/Security/UmbracoBackOfficeIdentityTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs @@ -1,16 +1,11 @@ using System; using System.Linq; using System.Security.Claims; -using System.Text; -using System.Threading.Tasks; -using System.Web.Security; -using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Security; -using Umbraco.Core.Services; +using Umbraco.Core.BackOffice; -namespace Umbraco.Tests.Security +namespace Umbraco.Tests.UnitTests.Umbraco.Core.BackOffice { [TestFixture] public class UmbracoBackOfficeIdentityTests diff --git a/src/Umbraco.Tests/Security/BackOfficeUserManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeUserManagerTests.cs index 30ed101297..99a2b323dd 100644 --- a/src/Umbraco.Tests/Security/BackOfficeUserManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeUserManagerTests.cs @@ -6,11 +6,11 @@ using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Owin.Security.DataProtection; using Moq; using NUnit.Framework; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Membership; using Umbraco.Net; -using Umbraco.Web.Models.Identity; -using Umbraco.Web.Security; +using BackOfficeUserManager = Umbraco.Web.Security.BackOfficeUserManager; namespace Umbraco.Tests.Security { diff --git a/src/Umbraco.Tests/Security/OwinDataProtectorTokenProviderTests.cs b/src/Umbraco.Tests/Security/OwinDataProtectorTokenProviderTests.cs index 5e9b731aa9..7b1ca53104 100644 --- a/src/Umbraco.Tests/Security/OwinDataProtectorTokenProviderTests.cs +++ b/src/Umbraco.Tests/Security/OwinDataProtectorTokenProviderTests.cs @@ -6,9 +6,9 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Owin.Security.DataProtection; using Moq; using NUnit.Framework; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Membership; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; namespace Umbraco.Tests.Security diff --git a/src/Umbraco.Tests/Security/UmbracoSecurityStampValidatorTests.cs b/src/Umbraco.Tests/Security/UmbracoSecurityStampValidatorTests.cs index 92fa032271..4adfe15ad7 100644 --- a/src/Umbraco.Tests/Security/UmbracoSecurityStampValidatorTests.cs +++ b/src/Umbraco.Tests/Security/UmbracoSecurityStampValidatorTests.cs @@ -11,18 +11,19 @@ using Microsoft.Owin.Security.Cookies; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Membership; using Umbraco.Net; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; + namespace Umbraco.Tests.Security { public class UmbracoSecurityStampValidatorTests { private Mock _mockOwinContext; - private Mock> _mockUserManager; + private Mock> _mockUserManager; private Mock _mockSignInManager; private AuthenticationTicket _testAuthTicket; @@ -34,7 +35,7 @@ namespace Umbraco.Tests.Security public void OnValidateIdentity_When_GetUserIdCallback_Is_Null_Expect_ArgumentNullException() { Assert.Throws(() => UmbracoSecurityStampValidator - .OnValidateIdentity, BackOfficeIdentityUser>( + .OnValidateIdentity, BackOfficeIdentityUser>( TimeSpan.MaxValue, null, null)); } @@ -42,7 +43,7 @@ namespace Umbraco.Tests.Security public async Task OnValidateIdentity_When_Validation_Interval_Not_Met_Expect_No_Op() { var func = UmbracoSecurityStampValidator - .OnValidateIdentity, BackOfficeIdentityUser>( + .OnValidateIdentity, BackOfficeIdentityUser>( TimeSpan.MaxValue, null, identity => throw new Exception()); _testAuthTicket.Properties.IssuedUtc = DateTimeOffset.UtcNow; @@ -61,11 +62,11 @@ namespace Umbraco.Tests.Security public void OnValidateIdentity_When_Time_To_Validate_But_No_UserManager_Expect_InvalidOperationException() { var func = UmbracoSecurityStampValidator - .OnValidateIdentity, BackOfficeIdentityUser>( + .OnValidateIdentity, BackOfficeIdentityUser>( TimeSpan.MinValue, null, identity => throw new Exception()); - _mockOwinContext.Setup(x => x.Get>(It.IsAny())) - .Returns((BackOfficeUserManager) null); + _mockOwinContext.Setup(x => x.Get>(It.IsAny())) + .Returns((Umbraco.Web.Security.BackOfficeUserManager) null); var context = new CookieValidateIdentityContext( _mockOwinContext.Object, @@ -79,7 +80,7 @@ namespace Umbraco.Tests.Security public void OnValidateIdentity_When_Time_To_Validate_But_No_SignInManager_Expect_InvalidOperationException() { var func = UmbracoSecurityStampValidator - .OnValidateIdentity, BackOfficeIdentityUser>( + .OnValidateIdentity, BackOfficeIdentityUser>( TimeSpan.MinValue, null, identity => throw new Exception()); _mockOwinContext.Setup(x => x.Get(It.IsAny())) @@ -99,7 +100,7 @@ namespace Umbraco.Tests.Security var userId = Guid.NewGuid().ToString(); var func = UmbracoSecurityStampValidator - .OnValidateIdentity, BackOfficeIdentityUser>( + .OnValidateIdentity, BackOfficeIdentityUser>( TimeSpan.MinValue, null, identity => userId); _mockUserManager.Setup(x => x.FindByIdAsync(userId)) @@ -122,7 +123,7 @@ namespace Umbraco.Tests.Security var userId = Guid.NewGuid().ToString(); var func = UmbracoSecurityStampValidator - .OnValidateIdentity, BackOfficeIdentityUser>( + .OnValidateIdentity, BackOfficeIdentityUser>( TimeSpan.MinValue, null, identity => userId); _mockUserManager.Setup(x => x.FindByIdAsync(userId)).ReturnsAsync(_testUser); @@ -145,7 +146,7 @@ namespace Umbraco.Tests.Security var userId = Guid.NewGuid().ToString(); var func = UmbracoSecurityStampValidator - .OnValidateIdentity, BackOfficeIdentityUser>( + .OnValidateIdentity, BackOfficeIdentityUser>( TimeSpan.MinValue, null, identity => userId); _mockUserManager.Setup(x => x.FindByIdAsync(userId)).ReturnsAsync(_testUser); @@ -169,7 +170,7 @@ namespace Umbraco.Tests.Security var userId = Guid.NewGuid().ToString(); var func = UmbracoSecurityStampValidator - .OnValidateIdentity, BackOfficeIdentityUser>( + .OnValidateIdentity, BackOfficeIdentityUser>( TimeSpan.MinValue, null, identity => userId); _mockUserManager.Setup(x => x.FindByIdAsync(userId)).ReturnsAsync(_testUser); @@ -193,7 +194,7 @@ namespace Umbraco.Tests.Security var expectedIdentity = new ClaimsIdentity(new List {new Claim("sub", "bob")}); var regenFuncCalled = false; - Func, BackOfficeIdentityUser, Task> regenFunc = + Func, BackOfficeIdentityUser, Task> regenFunc = (signInManager, userManager, user) => { regenFuncCalled = true; @@ -201,7 +202,7 @@ namespace Umbraco.Tests.Security }; var func = UmbracoSecurityStampValidator - .OnValidateIdentity, BackOfficeIdentityUser>( + .OnValidateIdentity, BackOfficeIdentityUser>( TimeSpan.MinValue, regenFunc, identity => userId); _mockUserManager.Setup(x => x.FindByIdAsync(userId)).ReturnsAsync(_testUser); @@ -249,7 +250,7 @@ namespace Umbraco.Tests.Security new AuthenticationProperties()); _testOptions = new CookieAuthenticationOptions { AuthenticationType = _testAuthType }; - _mockUserManager = new Mock>( + _mockUserManager = new Mock>( new Mock().Object, new Mock().Object, new Mock>().Object, @@ -266,7 +267,7 @@ namespace Umbraco.Tests.Security new Mock().Object); _mockOwinContext = new Mock(); - _mockOwinContext.Setup(x => x.Get>(It.IsAny())) + _mockOwinContext.Setup(x => x.Get>(It.IsAny())) .Returns(_mockUserManager.Object); _mockOwinContext.Setup(x => x.Get(It.IsAny())) .Returns(_mockSignInManager.Object); diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs index 5031d178bf..72941633e7 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs @@ -4,7 +4,7 @@ using Microsoft.Owin; using Microsoft.Owin.Security; using Microsoft.Owin.Security.Infrastructure; using Owin; -using Umbraco.Core.Security; +using Umbraco.Core.BackOffice; namespace Umbraco.Tests.TestHelpers.ControllerTesting { diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 66310caea9..793d31a5d3 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -8,6 +8,7 @@ using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using System.Web.Security; using Moq; +using Umbraco.Core.BackOffice; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Dictionary; diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 77aba483d7..22f5348501 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -147,10 +147,8 @@ - - @@ -275,7 +273,6 @@ - diff --git a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs index 8c40085e50..80deb5a800 100644 --- a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs @@ -28,7 +28,6 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.ControllerTesting; @@ -46,8 +45,8 @@ using Umbraco.Web.Routing; using Umbraco.Core.Media; using Umbraco.Net; using Umbraco.Persistance.SqlCe; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; +using BackOfficeIdentityUser = Umbraco.Core.BackOffice.BackOfficeIdentityUser; namespace Umbraco.Tests.Web.Controllers { diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index bce46b3129..d776749e6b 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -14,7 +14,6 @@ using Umbraco.Core.WebAssets; using Umbraco.Net; using Umbraco.Web.BackOffice.ActionResults; using Umbraco.Web.BackOffice.Filters; -using Umbraco.Web.BackOffice.Identity; using Umbraco.Web.Common.ActionResults; using Umbraco.Web.WebAssets; diff --git a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs index 9d3e2a9e3f..4325e10622 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ using System.Security.Claims; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Umbraco.Core; using Umbraco.Core.BackOffice; using Umbraco.Core.Mapping; @@ -14,14 +15,17 @@ namespace Umbraco.Extensions { public static void AddUmbracoBackOfficeIdentity(this IServiceCollection services) { + services.AddDataProtection(); + // UmbracoMapper - hack? - services.AddSingleton(); - services.AddSingleton(s => new MapDefinitionCollection(new[] {s.GetService()})); - services.AddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(s => new MapDefinitionCollection(new[] {s.GetService()})); + services.TryAddSingleton(); - services.AddScoped(); + services.TryAddScoped(); - services.AddIdentity(options => + services.AddIdentityCore(); + services.AddIdentityCore(options => { options.User.RequireUniqueEmail = true; @@ -43,9 +47,12 @@ namespace Umbraco.Extensions }) .AddDefaultTokenProviders() .AddUserStore() - .AddUserManager(); + .AddUserManager() + .AddClaimsPrincipalFactory>(); + + services.AddScoped(); + services.TryAddScoped>(); - // .AddClaimsPrincipalFactory>() // TODO: extract custom claims principal factory } } } diff --git a/src/Umbraco.Web/Compose/AuditEventsComponent.cs b/src/Umbraco.Web/Compose/AuditEventsComponent.cs index bd2520aa90..51c47233c7 100644 --- a/src/Umbraco.Web/Compose/AuditEventsComponent.cs +++ b/src/Umbraco.Web/Compose/AuditEventsComponent.cs @@ -8,10 +8,9 @@ using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Net; -using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; -using Umbraco.Web.Security; +using Umbraco.Extensions; namespace Umbraco.Core.Compose { diff --git a/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs b/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs index 21a242ee17..44d86df59c 100644 --- a/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs +++ b/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs @@ -1,7 +1,7 @@ using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Core.Composing; using Umbraco.Core.Mapping; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Models.Mapping; namespace Umbraco.Web.Composing.CompositionExtensions diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index 009d970fd5..b38bbbb19d 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -19,15 +19,14 @@ using Umbraco.Web.Mvc; using Umbraco.Web.Security; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using IUser = Umbraco.Core.Models.Membership.IUser; using Umbraco.Core.Mapping; -using Umbraco.Web.Models.Identity; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; using Umbraco.Web.Routing; namespace Umbraco.Web.Editors @@ -41,7 +40,7 @@ namespace Umbraco.Web.Editors [IsBackOffice] public class AuthenticationController : UmbracoApiController { - private BackOfficeUserManager _userManager; + private Security.BackOfficeUserManager _userManager; private BackOfficeSignInManager _signInManager; private readonly IUserPasswordConfiguration _passwordConfiguration; private readonly IHostingEnvironment _hostingEnvironment; @@ -72,8 +71,8 @@ namespace Umbraco.Web.Editors _requestAccessor = requestAccessor ?? throw new ArgumentNullException(nameof(securitySettings)); } - protected BackOfficeUserManager UserManager => _userManager - ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager()); + protected Security.BackOfficeUserManager UserManager => _userManager + ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager()); protected BackOfficeSignInManager SignInManager => _signInManager ?? (_signInManager = TryGetOwinContext().Result.GetBackOfficeSignInManager()); diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 825b67767a..782dea61ac 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -20,7 +20,6 @@ using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Core.Services; using Umbraco.Web.Features; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; using Constants = Umbraco.Core.Constants; using JArray = Newtonsoft.Json.Linq.JArray; @@ -32,6 +31,7 @@ using Umbraco.Core.Runtime; using Umbraco.Core.WebAssets; using Umbraco.Web.Trees; using Umbraco.Web.WebAssets; +using BackOfficeIdentityUser = Umbraco.Core.BackOffice.BackOfficeIdentityUser; namespace Umbraco.Web.Editors { diff --git a/src/Umbraco.Web/Editors/PasswordChanger.cs b/src/Umbraco.Web/Editors/PasswordChanger.cs index b6757f76cc..10a5248045 100644 --- a/src/Umbraco.Web/Editors/PasswordChanger.cs +++ b/src/Umbraco.Web/Editors/PasswordChanger.cs @@ -2,12 +2,10 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.Identity; -using Umbraco.Core.Security; using Umbraco.Web.Models; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; using IUser = Umbraco.Core.Models.Membership.IUser; @@ -34,7 +32,7 @@ namespace Umbraco.Web.Editors IUser currentUser, IUser savingUser, ChangingPasswordModel passwordModel, - BackOfficeUserManager userMgr) + Security.BackOfficeUserManager userMgr) { if (passwordModel == null) throw new ArgumentNullException(nameof(passwordModel)); if (userMgr == null) throw new ArgumentNullException(nameof(userMgr)); diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index e695139b8c..8a3156dd2d 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -11,6 +11,7 @@ using System.Web; using System.Web.Http; using System.Web.Mvc; using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.IO; @@ -24,7 +25,6 @@ using Umbraco.Core.Strings; using Umbraco.Web.Editors.Filters; using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; diff --git a/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs deleted file mode 100644 index 21c965aa3d..0000000000 --- a/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs +++ /dev/null @@ -1,434 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Models.Entities; -using Umbraco.Core.Models.Identity; -using Umbraco.Core.Models.Membership; - -namespace Umbraco.Web.Models.Identity -{ - public class BackOfficeIdentityUser : IdentityUser, IdentityUserClaim>, IRememberBeingDirty - { - private string _email; - private string _userName; - private int _id; - private bool _hasIdentity; - private DateTime? _lastLoginDateUtc; - private bool _emailConfirmed; - private string _name; - private int _accessFailedCount; - private string _passwordHash; - private string _culture; - private ObservableCollection _logins; - private Lazy> _getLogins; - private IReadOnlyUserGroup[] _groups; - private string[] _allowedSections; - private int[] _startMediaIds; - private int[] _startContentIds; - private DateTime? _lastPasswordChangeDateUtc; - - /// - /// Used to construct a new instance without an identity - /// - /// - /// This is allowed to be null (but would need to be filled in if trying to persist this instance) - /// - /// - public static BackOfficeIdentityUser CreateNew(IGlobalSettings globalSettings, string username, string email, string culture) - { - if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(username)); - if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(culture)); - - var user = new BackOfficeIdentityUser(globalSettings, Array.Empty()); - user.DisableChangeTracking(); - user._userName = username; - user._email = email; - //we are setting minvalue here because the default is "0" which is the id of the admin user - //which we cannot allow because the admin user will always exist - user._id = int.MinValue; - user._hasIdentity = false; - user._culture = culture; - user.EnableChangeTracking(); - return user; - } - - private BackOfficeIdentityUser(IGlobalSettings globalSettings, IReadOnlyUserGroup[] groups) - { - _startMediaIds = Array.Empty(); - _startContentIds = Array.Empty(); - _allowedSections = Array.Empty(); - _culture = globalSettings.DefaultUILanguage; - - // must initialize before setting groups - _roles = new ObservableCollection>(); - _roles.CollectionChanged += _roles_CollectionChanged; - - // use the property setters - they do more than just setting a field - Groups = groups; - } - - /// - /// Creates an existing user with the specified groups - /// - /// - /// - /// - public BackOfficeIdentityUser(IGlobalSettings globalSettings, int userId, IEnumerable groups) - : this(globalSettings, groups.ToArray()) - { - // use the property setters - they do more than just setting a field - Id = userId; - } - - /// - /// Returns true if an Id has been set on this object this will be false if the object is new and not persisted to the database - /// - public bool HasIdentity => _hasIdentity; - - public int[] CalculatedMediaStartNodeIds { get; internal set; } - public int[] CalculatedContentStartNodeIds { get; internal set; } - - public override int Id - { - get => _id; - set - { - _id = value; - _hasIdentity = true; - } - } - - /// - /// Override Email so we can track changes to it - /// - public override string Email - { - get => _email; - set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _email, nameof(Email)); - } - - /// - /// Override UserName so we can track changes to it - /// - public override string UserName - { - get => _userName; - set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _userName, nameof(UserName)); - } - - /// - /// LastPasswordChangeDateUtc so we can track changes to it - /// - public override DateTime? LastPasswordChangeDateUtc - { - get { return _lastPasswordChangeDateUtc; } - set { _beingDirty.SetPropertyValueAndDetectChanges(value, ref _lastPasswordChangeDateUtc, nameof(LastPasswordChangeDateUtc)); } - } - - /// - /// Override LastLoginDateUtc so we can track changes to it - /// - public override DateTime? LastLoginDateUtc - { - get => _lastLoginDateUtc; - set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _lastLoginDateUtc, nameof(LastLoginDateUtc)); - } - - /// - /// Override EmailConfirmed so we can track changes to it - /// - public override bool EmailConfirmed - { - get => _emailConfirmed; - set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _emailConfirmed, nameof(EmailConfirmed)); - } - - /// - /// Gets/sets the user's real name - /// - public string Name - { - get => _name; - set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _name, nameof(Name)); - } - - /// - /// Override AccessFailedCount so we can track changes to it - /// - public override int AccessFailedCount - { - get => _accessFailedCount; - set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _accessFailedCount, nameof(AccessFailedCount)); - } - - /// - /// Override PasswordHash so we can track changes to it - /// - public override string PasswordHash - { - get => _passwordHash; - set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _passwordHash, nameof(PasswordHash)); - } - - - /// - /// Content start nodes assigned to the User (not ones assigned to the user's groups) - /// - public int[] StartContentIds - { - get => _startContentIds; - set - { - if (value == null) value = new int[0]; - _beingDirty.SetPropertyValueAndDetectChanges(value, ref _startContentIds, nameof(StartContentIds), StartIdsComparer); - } - } - - /// - /// Media start nodes assigned to the User (not ones assigned to the user's groups) - /// - public int[] StartMediaIds - { - get => _startMediaIds; - set - { - if (value == null) value = new int[0]; - _beingDirty.SetPropertyValueAndDetectChanges(value, ref _startMediaIds, nameof(StartMediaIds), StartIdsComparer); - } - } - - /// - /// This is a readonly list of the user's allowed sections which are based on it's user groups - /// - public string[] AllowedSections - { - get { return _allowedSections ?? (_allowedSections = _groups.SelectMany(x => x.AllowedSections).Distinct().ToArray()); } - } - - public string Culture - { - get => _culture; - set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _culture, nameof(Culture)); - } - - public IReadOnlyUserGroup[] Groups - { - get => _groups; - set - { - //so they recalculate - _allowedSections = null; - - _groups = value; - - //now clear all roles and re-add them - _roles.CollectionChanged -= _roles_CollectionChanged; - _roles.Clear(); - foreach (var identityUserRole in _groups.Select(x => new IdentityUserRole - { - RoleId = x.Alias, - UserId = Id.ToString() - })) - { - _roles.Add(identityUserRole); - } - _roles.CollectionChanged += _roles_CollectionChanged; - - _beingDirty.SetPropertyValueAndDetectChanges(value, ref _groups, nameof(Groups), GroupsComparer); - } - } - - /// - /// Lockout is always enabled - /// - public override bool LockoutEnabled - { - get { return true; } - set - { - //do nothing - } - } - - /// - /// Based on the user's lockout end date, this will determine if they are locked out - /// - internal bool IsLockedOut - { - get - { - var isLocked = LockoutEndDateUtc.HasValue && LockoutEndDateUtc.Value.ToLocalTime() >= DateTime.Now; - return isLocked; - } - } - - /// - /// This is a 1:1 mapping with IUser.IsApproved - /// - internal bool IsApproved { get; set; } - - /// - /// Overridden to make the retrieval lazy - /// - public override ICollection Logins - { - get - { - if (_getLogins != null && _getLogins.IsValueCreated == false) - { - _logins = new ObservableCollection(); - foreach (var l in _getLogins.Value) - { - _logins.Add(l); - } - //now assign events - _logins.CollectionChanged += Logins_CollectionChanged; - } - return _logins; - } - } - - void Logins_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - _beingDirty.OnPropertyChanged(nameof(Logins)); - } - - private void _roles_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - _beingDirty.OnPropertyChanged(nameof(Roles)); - } - - private readonly ObservableCollection> _roles; - - /// - /// helper method to easily add a role without having to deal with IdentityUserRole{T} - /// - /// - /// - /// Adding a role this way will not reflect on the user's group's collection or it's allowed sections until the user is persisted - /// - public void AddRole(string role) - { - Roles.Add(new IdentityUserRole - { - UserId = Id.ToString(), - RoleId = role - }); - } - - /// - /// Override Roles because the value of these are the user's group aliases - /// - public override ICollection> Roles => _roles; - - /// - /// Used to set a lazy call back to populate the user's Login list - /// - /// - public void SetLoginsCallback(Lazy> callback) - { - _getLogins = callback ?? throw new ArgumentNullException(nameof(callback)); - } - - #region BeingDirty - - private readonly BeingDirty _beingDirty = new BeingDirty(); - - /// - public bool IsDirty() - { - return _beingDirty.IsDirty(); - } - - /// - public bool IsPropertyDirty(string propName) - { - return _beingDirty.IsPropertyDirty(propName); - } - - /// - public IEnumerable GetDirtyProperties() - { - return _beingDirty.GetDirtyProperties(); - } - - /// - public void ResetDirtyProperties() - { - _beingDirty.ResetDirtyProperties(); - } - - /// - public bool WasDirty() - { - return _beingDirty.WasDirty(); - } - - /// - public bool WasPropertyDirty(string propertyName) - { - return _beingDirty.WasPropertyDirty(propertyName); - } - - /// - public void ResetWereDirtyProperties() - { - _beingDirty.ResetWereDirtyProperties(); - } - - /// - public void ResetDirtyProperties(bool rememberDirty) - { - _beingDirty.ResetDirtyProperties(rememberDirty); - } - - /// - public IEnumerable GetWereDirtyProperties() - => _beingDirty.GetWereDirtyProperties(); - - /// - /// Disables change tracking. - /// - public void DisableChangeTracking() - { - _beingDirty.DisableChangeTracking(); - } - - /// - /// Enables change tracking. - /// - public void EnableChangeTracking() - { - _beingDirty.EnableChangeTracking(); - } - - public event PropertyChangedEventHandler PropertyChanged - { - add - { - _beingDirty.PropertyChanged += value; - } - remove - { - _beingDirty.PropertyChanged -= value; - } - } - - #endregion - - //Custom comparer for enumerables - private static readonly DelegateEqualityComparer GroupsComparer = new DelegateEqualityComparer( - (groups, enumerable) => groups.Select(x => x.Alias).UnsortedSequenceEqual(enumerable.Select(x => x.Alias)), - groups => groups.GetHashCode()); - - private static readonly DelegateEqualityComparer StartIdsComparer = new DelegateEqualityComparer( - (groups, enumerable) => groups.UnsortedSequenceEqual(enumerable), - groups => groups.GetHashCode()); - - } -} diff --git a/src/Umbraco.Web/Models/Identity/IdentityMapDefinition.cs b/src/Umbraco.Web/Models/Identity/IdentityMapDefinition.cs deleted file mode 100644 index 7c20c6108a..0000000000 --- a/src/Umbraco.Web/Models/Identity/IdentityMapDefinition.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Mapping; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; - -namespace Umbraco.Web.Models.Identity -{ - public class IdentityMapDefinition : IMapDefinition - { - private readonly ILocalizedTextService _textService; - private readonly IEntityService _entityService; - private readonly IGlobalSettings _globalSettings; - - public IdentityMapDefinition(ILocalizedTextService textService, IEntityService entityService, IGlobalSettings globalSettings) - { - _textService = textService; - _entityService = entityService; - _globalSettings = globalSettings; - } - - public void DefineMaps(UmbracoMapper mapper) - { - mapper.Define( - (source, context) => - { - var target = new BackOfficeIdentityUser(_globalSettings, source.Id, source.Groups); - target.DisableChangeTracking(); - return target; - }, - (source, target, context) => - { - Map(source, target); - target.ResetDirtyProperties(true); - target.EnableChangeTracking(); - }); - } - - // Umbraco.Code.MapAll -Id -Groups -LockoutEnabled -PhoneNumber -PhoneNumberConfirmed -TwoFactorEnabled - private void Map(IUser source, BackOfficeIdentityUser target) - { - // well, the ctor has been fixed - /* - // these two are already set in ctor but BackOfficeIdentityUser ctor is CompletelyBroken - target.Id = source.Id; - target.Groups = source.Groups.ToArray(); - */ - - target.CalculatedMediaStartNodeIds = source.CalculateMediaStartNodeIds(_entityService); - target.CalculatedContentStartNodeIds = source.CalculateContentStartNodeIds(_entityService); - target.Email = source.Email; - target.UserName = source.Username; - target.LastPasswordChangeDateUtc = source.LastPasswordChangeDate.ToUniversalTime(); - target.LastLoginDateUtc = source.LastLoginDate.ToUniversalTime(); - target.EmailConfirmed = source.EmailConfirmedDate.HasValue; - target.Name = source.Name; - target.AccessFailedCount = source.FailedPasswordAttempts; - target.PasswordHash = GetPasswordHash(source.RawPasswordValue); - target.StartContentIds = source.StartContentIds; - target.StartMediaIds = source.StartMediaIds; - target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); // project CultureInfo to string - target.IsApproved = source.IsApproved; - target.SecurityStamp = source.SecurityStamp; - target.LockoutEndDateUtc = source.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?) null; - - // this was in AutoMapper but does not have a setter anyways - //target.AllowedSections = source.AllowedSections.ToArray(), - - // these were marked as ignored for AutoMapper but don't have a setter anyways - //target.Logins =; - //target.Claims =; - //target.Roles =; - } - - private static string GetPasswordHash(string storedPass) - { - return storedPass.StartsWith(Constants.Security.EmptyPasswordPrefix) ? null : storedPass; - } - } -} diff --git a/src/Umbraco.Web/Models/Identity/IdentityUser.cs b/src/Umbraco.Web/Models/Identity/IdentityUser.cs deleted file mode 100644 index 7bd077e879..0000000000 --- a/src/Umbraco.Web/Models/Identity/IdentityUser.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Models.Identity; - -namespace Umbraco.Web.Models.Identity -{ - /// - /// Default IUser implementation - /// - /// - /// - /// This class normally exists inside of the EntityFramework library, not sure why MS chose to explicitly put it there but we don't want - /// references to that so we will create our own here - /// - public class IdentityUser - where TLogin : IIdentityUserLogin - //NOTE: Making our role id a string - where TRole : IdentityUserRole - where TClaim : IdentityUserClaim - { - /// - /// Initializes a new instance of the class. - /// - public IdentityUser() - { - Claims = new List(); - Roles = new List(); - Logins = new List(); - } - - /// - /// Last login date - /// - public virtual DateTime? LastLoginDateUtc { get; set; } - - /// - /// Email - /// - public virtual string Email { get; set; } - - /// - /// True if the email is confirmed, default is false - /// - public virtual bool EmailConfirmed { get; set; } - - /// - /// The salted/hashed form of the user password - /// - public virtual string PasswordHash { get; set; } - - /// - /// A random value that should change whenever a users credentials have changed (password changed, login removed) - /// - public virtual string SecurityStamp { get; set; } - - /// - /// PhoneNumber for the user - /// - public virtual string PhoneNumber { get; set; } - - /// - /// True if the phone number is confirmed, default is false - /// - public virtual bool PhoneNumberConfirmed { get; set; } - - /// - /// Is two factor enabled for the user - /// - public virtual bool TwoFactorEnabled { get; set; } - - /// - /// DateTime in UTC when lockout ends, any time in the past is considered not locked out. - /// - public virtual DateTime? LockoutEndDateUtc { get; set; } - - /// - /// DateTime in UTC when the password was last changed. - /// - public virtual DateTime? LastPasswordChangeDateUtc { get; set; } - - /// - /// Is lockout enabled for this user - /// - public virtual bool LockoutEnabled { get; set; } - - /// - /// Used to record failures for the purposes of lockout - /// - public virtual int AccessFailedCount { get; set; } - - /// - /// Navigation property for user roles - /// - public virtual ICollection Roles { get; } - - /// - /// Navigation property for user claims - /// - public virtual ICollection Claims { get; } - - /// - /// Navigation property for user logins - /// - public virtual ICollection Logins { get; } - - /// - /// User ID (Primary Key) - /// - public virtual TKey Id { get; set; } - - /// - /// User name - /// - public virtual string UserName { get; set; } - } -} diff --git a/src/Umbraco.Web/Models/Identity/UserLoginInfoWrapper.cs b/src/Umbraco.Web/Models/Identity/UserLoginInfoWrapper.cs deleted file mode 100644 index 336b4c9e72..0000000000 --- a/src/Umbraco.Web/Models/Identity/UserLoginInfoWrapper.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.AspNetCore.Identity; -using Umbraco.Core.Models.Identity; - -namespace Umbraco.Web.Models.Identity -{ - internal class UserLoginInfoWrapper : IUserLoginInfo - { - private readonly UserLoginInfo _info; - - public static IUserLoginInfo Wrap(UserLoginInfo info) => new UserLoginInfoWrapper(info); - - private UserLoginInfoWrapper(UserLoginInfo info) - { - _info = info; - } - - public string LoginProvider - { - get => _info.LoginProvider; - set => _info.LoginProvider = value; - } - - public string ProviderKey - { - get => _info.ProviderKey; - set => _info.ProviderKey = value; - } - } -} diff --git a/src/Umbraco.Web/OwinExtensions.cs b/src/Umbraco.Web/OwinExtensions.cs index 52c1187707..67e9375ab5 100644 --- a/src/Umbraco.Web/OwinExtensions.cs +++ b/src/Umbraco.Web/OwinExtensions.cs @@ -3,8 +3,8 @@ using System.Web; using Microsoft.Owin; using Microsoft.Owin.Security; using Umbraco.Core; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; +using BackOfficeIdentityUser = Umbraco.Core.BackOffice.BackOfficeIdentityUser; namespace Umbraco.Web { diff --git a/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs b/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs index af7aebce3a..930cabeee3 100644 --- a/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs +++ b/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs @@ -1,8 +1,8 @@ using System; using System.DirectoryServices.AccountManagement; using System.Threading.Tasks; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; -using Umbraco.Web.Models.Identity; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/AppBuilderExtensions.cs b/src/Umbraco.Web/Security/AppBuilderExtensions.cs index 3f129f941d..6254dabeb9 100644 --- a/src/Umbraco.Web/Security/AppBuilderExtensions.cs +++ b/src/Umbraco.Web/Security/AppBuilderExtensions.cs @@ -11,6 +11,7 @@ using Microsoft.Owin.Security.DataHandler; using Microsoft.Owin.Security.DataProtection; using Owin; using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; @@ -19,7 +20,6 @@ using Umbraco.Core.Mapping; using Umbraco.Net; using Umbraco.Core.Services; using Umbraco.Web.Composing; -using Umbraco.Web.Models.Identity; using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Security diff --git a/src/Umbraco.Web/Security/AuthenticationExtensions.cs b/src/Umbraco.Web/Security/AuthenticationExtensions.cs index 7d76eaa7be..ee3b69c389 100644 --- a/src/Umbraco.Web/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Web/Security/AuthenticationExtensions.cs @@ -12,8 +12,9 @@ using Microsoft.Owin; using Microsoft.Owin.Security; using Newtonsoft.Json; using Umbraco.Core; +using Umbraco.Core.BackOffice; +using Umbraco.Extensions; using Umbraco.Web.Composing; -using Umbraco.Core.Security; using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Security @@ -333,38 +334,6 @@ namespace Umbraco.Web.Security return secureDataFormat.Unprotect(formsCookie); } - /// - /// This will return the current back office identity if the IPrincipal is the correct type - /// - /// - /// - public static UmbracoBackOfficeIdentity GetUmbracoIdentity(this IPrincipal user) - { - //If it's already a UmbracoBackOfficeIdentity - if (user.Identity is UmbracoBackOfficeIdentity backOfficeIdentity) return backOfficeIdentity; - - //Check if there's more than one identity assigned and see if it's a UmbracoBackOfficeIdentity and use that - if (user is ClaimsPrincipal claimsPrincipal) - { - backOfficeIdentity = claimsPrincipal.Identities.OfType().FirstOrDefault(); - if (backOfficeIdentity != null) return backOfficeIdentity; - } - - //Otherwise convert to a UmbracoBackOfficeIdentity if it's auth'd and has the back office session - if (user.Identity is ClaimsIdentity claimsIdentity && claimsIdentity.IsAuthenticated && claimsIdentity.HasClaim(x => x.Type == Constants.Security.SessionIdClaimType)) - { - try - { - return UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity); - } - catch (InvalidOperationException) - { - } - } - - return null; - } - /// /// Ensures that the thread culture is set based on the back office user's culture /// diff --git a/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs b/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs index 01f8dc4e96..35edb251f9 100644 --- a/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs +++ b/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs @@ -4,10 +4,8 @@ using System.Threading.Tasks; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Umbraco.Core; -using Umbraco.Web.Composing; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Hosting; @@ -77,12 +75,12 @@ namespace Umbraco.Web.Security Expires = DateTime.Now.AddYears(-1), Path = "/" }); - context.Response.Cookies.Append(Core.Constants.Web.PreviewCookieName, "", new CookieOptions + context.Response.Cookies.Append(Constants.Web.PreviewCookieName, "", new CookieOptions { Expires = DateTime.Now.AddYears(-1), Path = "/" }); - context.Response.Cookies.Append(Core.Constants.Security.BackOfficeExternalCookieName, "", new CookieOptions + context.Response.Cookies.Append(Constants.Security.BackOfficeExternalCookieName, "", new CookieOptions { Expires = DateTime.Now.AddYears(-1), Path = "/" diff --git a/src/Umbraco.Web/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web/Security/BackOfficeSignInManager.cs index bbb4328fc3..5e172d2d77 100644 --- a/src/Umbraco.Web/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web/Security/BackOfficeSignInManager.cs @@ -8,9 +8,9 @@ using Microsoft.Owin; using Microsoft.Owin.Logging; using Microsoft.Owin.Security; using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; -using Umbraco.Core.Security; -using Umbraco.Web.Models.Identity; +using Umbraco.Core.BackOffice; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/BackOfficeUserManager.cs b/src/Umbraco.Web/Security/BackOfficeUserManager.cs index a143029327..ed2df536e7 100644 --- a/src/Umbraco.Web/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Web/Security/BackOfficeUserManager.cs @@ -8,12 +8,12 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Owin.Security.DataProtection; using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; using Umbraco.Core.Mapping; using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Net; -using Umbraco.Web.Models.Identity; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs b/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs index b3eb9da3d5..d9d3f0e6f6 100644 --- a/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs +++ b/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs @@ -1,6 +1,6 @@ using System; using Microsoft.Owin; -using Umbraco.Web.Models.Identity; +using Umbraco.Core.BackOffice; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/BackOfficeUserStore.cs b/src/Umbraco.Web/Security/BackOfficeUserStore.cs index 6f401163aa..d9d1900951 100644 --- a/src/Umbraco.Web/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Web/Security/BackOfficeUserStore.cs @@ -6,14 +6,13 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Models.Identity; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Web.Models.Identity; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/BackOfficeUserValidator.cs b/src/Umbraco.Web/Security/BackOfficeUserValidator.cs index 94e9c2e0bd..951bdee4ed 100644 --- a/src/Umbraco.Web/Security/BackOfficeUserValidator.cs +++ b/src/Umbraco.Web/Security/BackOfficeUserValidator.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; -using Umbraco.Web.Models.Identity; +using Umbraco.Core.BackOffice; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/ExternalSignInAutoLinkOptions.cs b/src/Umbraco.Web/Security/ExternalSignInAutoLinkOptions.cs index abe5aeb196..fc84cd270d 100644 --- a/src/Umbraco.Web/Security/ExternalSignInAutoLinkOptions.cs +++ b/src/Umbraco.Web/Security/ExternalSignInAutoLinkOptions.cs @@ -1,7 +1,7 @@ using System; using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Web.Composing; -using Umbraco.Web.Models.Identity; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/FixWindowsAuthMiddlware.cs b/src/Umbraco.Web/Security/FixWindowsAuthMiddlware.cs index 30038e1f31..9bad4bec17 100644 --- a/src/Umbraco.Web/Security/FixWindowsAuthMiddlware.cs +++ b/src/Umbraco.Web/Security/FixWindowsAuthMiddlware.cs @@ -4,7 +4,7 @@ using System.Security.Principal; using System.Threading.Tasks; using Microsoft.Owin; using Umbraco.Core; -using Umbraco.Core.Security; +using Umbraco.Core.BackOffice; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/IBackOfficeUserManagerMarker.cs b/src/Umbraco.Web/Security/IBackOfficeUserManagerMarker.cs index bfe2590eb8..2b1f9927f2 100644 --- a/src/Umbraco.Web/Security/IBackOfficeUserManagerMarker.cs +++ b/src/Umbraco.Web/Security/IBackOfficeUserManagerMarker.cs @@ -1,5 +1,5 @@ using Microsoft.Owin; -using Umbraco.Web.Models.Identity; +using Umbraco.Core.BackOffice; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/IBackOfficeUserPasswordChecker.cs b/src/Umbraco.Web/Security/IBackOfficeUserPasswordChecker.cs index 9b9e7443be..2fae308eb0 100644 --- a/src/Umbraco.Web/Security/IBackOfficeUserPasswordChecker.cs +++ b/src/Umbraco.Web/Security/IBackOfficeUserPasswordChecker.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; -using Umbraco.Core.Models.Identity; -using Umbraco.Web.Models.Identity; +using Umbraco.Core.BackOffice; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/IUserSessionStore.cs b/src/Umbraco.Web/Security/IUserSessionStore.cs deleted file mode 100644 index 06b7c2f165..0000000000 --- a/src/Umbraco.Web/Security/IUserSessionStore.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Identity; - -namespace Umbraco.Core.Security -{ - /// - /// An IUserStore interface part to implement if the store supports validating user session Ids - /// - /// - public interface IUserSessionStore : IUserStore - where TUser : class - { - Task ValidateSessionIdAsync(string userId, string sessionId); - } -} diff --git a/src/Umbraco.Web/Security/IdentityAuditEventArgs.cs b/src/Umbraco.Web/Security/IdentityAuditEventArgs.cs index 5847250b41..d37974276c 100644 --- a/src/Umbraco.Web/Security/IdentityAuditEventArgs.cs +++ b/src/Umbraco.Web/Security/IdentityAuditEventArgs.cs @@ -1,5 +1,6 @@ using System; using System.Threading; +using Umbraco.Extensions; namespace Umbraco.Web.Security diff --git a/src/Umbraco.Web/Security/NopLookupNormalizer.cs b/src/Umbraco.Web/Security/NopLookupNormalizer.cs deleted file mode 100644 index 08aa8d548a..0000000000 --- a/src/Umbraco.Web/Security/NopLookupNormalizer.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.AspNetCore.Identity; - -namespace Umbraco.Web.Security -{ - /// - /// No-op lookup normalizer to maintain compatibility with ASP.NET Identity 2 - /// - public class NopLookupNormalizer : ILookupNormalizer - { - public string NormalizeName(string name) => name; - - public string NormalizeEmail(string email) => email; - } -} diff --git a/src/Umbraco.Web/Security/OwinDataProtectorTokenProvider.cs b/src/Umbraco.Web/Security/OwinDataProtectorTokenProvider.cs index 15bd4dfd75..72e12b8621 100644 --- a/src/Umbraco.Web/Security/OwinDataProtectorTokenProvider.cs +++ b/src/Umbraco.Web/Security/OwinDataProtectorTokenProvider.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.Owin.Security.DataProtection; -using Umbraco.Web.Models.Identity; +using Umbraco.Core.BackOffice; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/PreviewAuthenticationMiddleware.cs b/src/Umbraco.Web/Security/PreviewAuthenticationMiddleware.cs index 2f9648d1f7..799edb5f60 100644 --- a/src/Umbraco.Web/Security/PreviewAuthenticationMiddleware.cs +++ b/src/Umbraco.Web/Security/PreviewAuthenticationMiddleware.cs @@ -1,12 +1,10 @@ using System.Security.Claims; using System.Threading.Tasks; -using System.Web; using Microsoft.Owin; using Umbraco.Core; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; -using Umbraco.Core.Security; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/UmbracoBackOfficeIdentity.cs b/src/Umbraco.Web/Security/UmbracoBackOfficeIdentity.cs deleted file mode 100644 index c3697d5e9e..0000000000 --- a/src/Umbraco.Web/Security/UmbracoBackOfficeIdentity.cs +++ /dev/null @@ -1,232 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using Umbraco.Web; - -namespace Umbraco.Core.Security -{ - - /// - /// A custom user identity for the Umbraco backoffice - /// - /// - /// This inherits from FormsIdentity for backwards compatibility reasons since we still support the forms auth cookie, in v8 we can - /// change over to 'pure' asp.net identity and just inherit from ClaimsIdentity. - /// - [Serializable] - public class UmbracoBackOfficeIdentity : ClaimsIdentity - { - public static UmbracoBackOfficeIdentity FromClaimsIdentity(ClaimsIdentity identity) - { - return new UmbracoBackOfficeIdentity(identity); - } - - /// - /// Creates a new UmbracoBackOfficeIdentity - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public UmbracoBackOfficeIdentity(int userId, string username, string realName, - IEnumerable startContentNodes, IEnumerable startMediaNodes, string culture, - string sessionId, string securityStamp, IEnumerable allowedApps, IEnumerable roles) - : base(Enumerable.Empty(), Constants.Security.BackOfficeAuthenticationType) //this ctor is used to ensure the IsAuthenticated property is true - { - if (allowedApps == null) throw new ArgumentNullException(nameof(allowedApps)); - if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(username)); - if (string.IsNullOrWhiteSpace(realName)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(realName)); - if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(culture)); - if (string.IsNullOrWhiteSpace(sessionId)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(sessionId)); - if (string.IsNullOrWhiteSpace(securityStamp)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(securityStamp)); - AddRequiredClaims(userId, username, realName, startContentNodes, startMediaNodes, culture, sessionId, securityStamp, allowedApps, roles); - } - - /// - /// Creates a new UmbracoBackOfficeIdentity - /// - /// - /// The original identity created by the ClaimsIdentityFactory - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public UmbracoBackOfficeIdentity(ClaimsIdentity childIdentity, - int userId, string username, string realName, - IEnumerable startContentNodes, IEnumerable startMediaNodes, string culture, - string sessionId, string securityStamp, IEnumerable allowedApps, IEnumerable roles) - : base(childIdentity.Claims, Constants.Security.BackOfficeAuthenticationType) - { - if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(username)); - if (string.IsNullOrWhiteSpace(realName)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(realName)); - if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(culture)); - if (string.IsNullOrWhiteSpace(sessionId)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(sessionId)); - if (string.IsNullOrWhiteSpace(securityStamp)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(securityStamp)); - Actor = childIdentity; - AddRequiredClaims(userId, username, realName, startContentNodes, startMediaNodes, culture, sessionId, securityStamp, allowedApps, roles); - } - - /// - /// Create a back office identity based on an existing claims identity - /// - /// - private UmbracoBackOfficeIdentity(ClaimsIdentity identity) - : base(identity.Claims, Constants.Security.BackOfficeAuthenticationType) - { - Actor = identity; - - //validate that all claims exist - foreach (var t in RequiredBackOfficeIdentityClaimTypes) - { - //if the identity doesn't have the claim, or the claim value is null - if (identity.HasClaim(x => x.Type == t) == false || identity.HasClaim(x => x.Type == t && x.Value.IsNullOrWhiteSpace())) - { - throw new InvalidOperationException("Cannot create a " + typeof(UmbracoBackOfficeIdentity) + " from " + typeof(ClaimsIdentity) + " since the required claim " + t + " is missing"); - } - } - } - - public const string Issuer = Constants.Security.BackOfficeAuthenticationType; - - /// - /// Returns the required claim types for a back office identity - /// - /// - /// This does not include the role claim type or allowed apps type since that is a collection and in theory could be empty - /// - public static IEnumerable RequiredBackOfficeIdentityClaimTypes => new[] - { - ClaimTypes.NameIdentifier, //id - ClaimTypes.Name, //username - ClaimTypes.GivenName, - Constants.Security.StartContentNodeIdClaimType, - Constants.Security.StartMediaNodeIdClaimType, - ClaimTypes.Locality, - Constants.Security.SessionIdClaimType, - Constants.Web.SecurityStampClaimType - }; - - /// - /// Adds claims based on the ctor data - /// - private void AddRequiredClaims(int userId, string username, string realName, - IEnumerable startContentNodes, IEnumerable startMediaNodes, string culture, - string sessionId, string securityStamp, IEnumerable allowedApps, IEnumerable roles) - { - //This is the id that 'identity' uses to check for the user id - if (HasClaim(x => x.Type == ClaimTypes.NameIdentifier) == false) - AddClaim(new Claim(ClaimTypes.NameIdentifier, userId.ToInvariantString(), ClaimValueTypes.Integer32, Issuer, Issuer, this)); - - if (HasClaim(x => x.Type == ClaimTypes.Name) == false) - AddClaim(new Claim(ClaimTypes.Name, username, ClaimValueTypes.String, Issuer, Issuer, this)); - - if (HasClaim(x => x.Type == ClaimTypes.GivenName) == false) - AddClaim(new Claim(ClaimTypes.GivenName, realName, ClaimValueTypes.String, Issuer, Issuer, this)); - - if (HasClaim(x => x.Type == Constants.Security.StartContentNodeIdClaimType) == false && startContentNodes != null) - { - foreach (var startContentNode in startContentNodes) - { - AddClaim(new Claim(Constants.Security.StartContentNodeIdClaimType, startContentNode.ToInvariantString(), ClaimValueTypes.Integer32, Issuer, Issuer, this)); - } - } - - if (HasClaim(x => x.Type == Constants.Security.StartMediaNodeIdClaimType) == false && startMediaNodes != null) - { - foreach (var startMediaNode in startMediaNodes) - { - AddClaim(new Claim(Constants.Security.StartMediaNodeIdClaimType, startMediaNode.ToInvariantString(), ClaimValueTypes.Integer32, Issuer, Issuer, this)); - } - } - - if (HasClaim(x => x.Type == ClaimTypes.Locality) == false) - AddClaim(new Claim(ClaimTypes.Locality, culture, ClaimValueTypes.String, Issuer, Issuer, this)); - - if (HasClaim(x => x.Type == Constants.Security.SessionIdClaimType) == false && SessionId.IsNullOrWhiteSpace() == false) - AddClaim(new Claim(Constants.Security.SessionIdClaimType, sessionId, ClaimValueTypes.String, Issuer, Issuer, this)); - - //The security stamp claim is also required... this is because this claim type is hard coded - // by the SecurityStampValidator, see: https://katanaproject.codeplex.com/workitem/444 - if (HasClaim(x => x.Type == Constants.Web.SecurityStampClaimType) == false) - AddClaim(new Claim(Constants.Web.SecurityStampClaimType, securityStamp, ClaimValueTypes.String, Issuer, Issuer, this)); - - //Add each app as a separate claim - if (HasClaim(x => x.Type == Constants.Security.AllowedApplicationsClaimType) == false && allowedApps != null) - { - foreach (var application in allowedApps) - { - AddClaim(new Claim(Constants.Security.AllowedApplicationsClaimType, application, ClaimValueTypes.String, Issuer, Issuer, this)); - } - } - - //Claims are added by the ClaimsIdentityFactory because our UserStore supports roles, however this identity might - // not be made with that factory if it was created with a different ticket so perform the check - if (HasClaim(x => x.Type == DefaultRoleClaimType) == false && roles != null) - { - //manually add them - foreach (var roleName in roles) - { - AddClaim(new Claim(RoleClaimType, roleName, ClaimValueTypes.String, Issuer, Issuer, this)); - } - } - - } - - /// - /// - /// Gets the type of authenticated identity. - /// - /// - /// The type of authenticated identity. This property always returns "UmbracoBackOffice". - /// - public override string AuthenticationType => Issuer; - - private int[] _startContentNodes; - public int[] StartContentNodes => _startContentNodes ?? (_startContentNodes = FindAll(x => x.Type == Constants.Security.StartContentNodeIdClaimType).Select(app => int.TryParse(app.Value, out var i) ? i : default).Where(x => x != default).ToArray()); - - private int[] _startMediaNodes; - public int[] StartMediaNodes => _startMediaNodes ?? (_startMediaNodes = FindAll(x => x.Type == Constants.Security.StartMediaNodeIdClaimType).Select(app => int.TryParse(app.Value, out var i) ? i : default).Where(x => x != default).ToArray()); - - private string[] _allowedApplications; - public string[] AllowedApplications => _allowedApplications ?? (_allowedApplications = FindAll(x => x.Type == Constants.Security.AllowedApplicationsClaimType).Select(app => app.Value).ToArray()); - - public int Id => int.Parse(this.FindFirstValue(ClaimTypes.NameIdentifier)); - - public string RealName => this.FindFirstValue(ClaimTypes.GivenName); - - public string Username => this.FindFirstValue(ClaimTypes.Name); - - public string Culture => this.FindFirstValue(ClaimTypes.Locality); - - public string SessionId - { - get => this.FindFirstValue(Constants.Security.SessionIdClaimType); - set - { - var existing = FindFirst(Constants.Security.SessionIdClaimType); - if (existing != null) - TryRemoveClaim(existing); - AddClaim(new Claim(Constants.Security.SessionIdClaimType, value, ClaimValueTypes.String, Issuer, Issuer, this)); - } - } - - public string SecurityStamp => this.FindFirstValue(Constants.Web.SecurityStampClaimType); - - public string[] Roles => this.FindAll(x => x.Type == DefaultRoleClaimType).Select(role => role.Value).ToArray(); - - } -} diff --git a/src/Umbraco.Web/Security/UmbracoSecureDataFormat.cs b/src/Umbraco.Web/Security/UmbracoSecureDataFormat.cs index 19cd602657..41a2ad3bba 100644 --- a/src/Umbraco.Web/Security/UmbracoSecureDataFormat.cs +++ b/src/Umbraco.Web/Security/UmbracoSecureDataFormat.cs @@ -1,6 +1,6 @@ using System; using Microsoft.Owin.Security; -using Umbraco.Core.Security; +using Umbraco.Core.BackOffice; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/UmbracoSecurityStampValidator.cs b/src/Umbraco.Web/Security/UmbracoSecurityStampValidator.cs index a3f78f5262..18539d8fab 100644 --- a/src/Umbraco.Web/Security/UmbracoSecurityStampValidator.cs +++ b/src/Umbraco.Web/Security/UmbracoSecurityStampValidator.cs @@ -3,7 +3,7 @@ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.Owin.Security.Cookies; using Umbraco.Core; -using Umbraco.Web.Models.Identity; +using Umbraco.Core.BackOffice; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/UserAwarePasswordHasher.cs b/src/Umbraco.Web/Security/UserAwarePasswordHasher.cs index d804ef0ae4..b1d88348d0 100644 --- a/src/Umbraco.Web/Security/UserAwarePasswordHasher.cs +++ b/src/Umbraco.Web/Security/UserAwarePasswordHasher.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Identity; +using Umbraco.Core.BackOffice; using Umbraco.Core.Security; -using Umbraco.Web.Models.Identity; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index 8975f9cde0..3c17bd370a 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -5,11 +5,10 @@ using Umbraco.Core; using Umbraco.Core.Services; using Umbraco.Core.Models.Membership; using Microsoft.Owin; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; using Umbraco.Core.Models; -using Umbraco.Web.Models.Identity; namespace Umbraco.Web.Security { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index b7c1e6e0b5..9817431f76 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -176,10 +176,6 @@ - - - - @@ -198,7 +194,6 @@ - @@ -207,14 +202,11 @@ - - - diff --git a/src/Umbraco.Web/WebApi/Filters/CheckIfUserTicketDataIsStaleAttribute.cs b/src/Umbraco.Web/WebApi/Filters/CheckIfUserTicketDataIsStaleAttribute.cs index c68b949bba..09fd5e080c 100644 --- a/src/Umbraco.Web/WebApi/Filters/CheckIfUserTicketDataIsStaleAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/CheckIfUserTicketDataIsStaleAttribute.cs @@ -6,13 +6,12 @@ using System.Web.Http.Controllers; using System.Web.Http.Filters; using Umbraco.Core; using Umbraco.Web.Composing; -using Umbraco.Core.Models.Identity; +using Umbraco.Core.BackOffice; using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; using Umbraco.Web.Security; using Umbraco.Core.Mapping; using Umbraco.Core.Models; -using Umbraco.Web.Models.Identity; namespace Umbraco.Web.WebApi.Filters { diff --git a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs index 2691063f19..b171356e23 100644 --- a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs @@ -2,10 +2,10 @@ using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; -using Umbraco.Web.WebApi.Filters;using Umbraco.Core.Models.Identity; +using Umbraco.Web.WebApi.Filters; +using Umbraco.Core.BackOffice; using Umbraco.Core.Persistence; using Umbraco.Core.Services; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; using Umbraco.Core.Mapping; using Umbraco.Web.Routing; @@ -30,7 +30,7 @@ namespace Umbraco.Web.WebApi [EnableDetailedErrors] public abstract class UmbracoAuthorizedApiController : UmbracoApiController { - private BackOfficeUserManager _userManager; + private Security.BackOfficeUserManager _userManager; protected UmbracoAuthorizedApiController() { @@ -44,7 +44,7 @@ namespace Umbraco.Web.WebApi /// /// Gets the user manager. /// - protected BackOfficeUserManager UserManager + protected Security.BackOfficeUserManager UserManager => _userManager ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager()); } }