diff --git a/src/Umbraco.Web/Models/Identity/IdentityUser.cs b/src/Umbraco.Core/Models/Identity/IdentityUser.cs similarity index 98% rename from src/Umbraco.Web/Models/Identity/IdentityUser.cs rename to src/Umbraco.Core/Models/Identity/IdentityUser.cs index 7bd077e879..093e42c1e7 100644 --- a/src/Umbraco.Web/Models/Identity/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.Web.Models.Identity +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.Web/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Infrastructure/BackOffice/BackOfficeIdentityUser.cs similarity index 98% rename from src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs rename to src/Umbraco.Infrastructure/BackOffice/BackOfficeIdentityUser.cs index 21c965aa3d..ea160ef1cf 100644 --- a/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs +++ b/src/Umbraco.Infrastructure/BackOffice/BackOfficeIdentityUser.cs @@ -4,13 +4,12 @@ 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 +namespace Umbraco.Core.BackOffice { public class BackOfficeIdentityUser : IdentityUser, IdentityUserClaim>, IRememberBeingDirty { @@ -90,8 +89,8 @@ namespace Umbraco.Web.Models.Identity /// 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 { @@ -258,7 +257,7 @@ namespace Umbraco.Web.Models.Identity /// /// Based on the user's lockout end date, this will determine if they are locked out /// - internal bool IsLockedOut + public bool IsLockedOut { get { @@ -270,7 +269,7 @@ namespace Umbraco.Web.Models.Identity /// /// 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.Web/Security/BackOfficeUserManager.cs b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserManager.cs similarity index 70% rename from src/Umbraco.Web/Security/BackOfficeUserManager.cs rename to src/Umbraco.Infrastructure/BackOffice/BackOfficeUserManager.cs index a143029327..b98a9bb68c 100644 --- a/src/Umbraco.Web/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserManager.cs @@ -1,143 +1,42 @@ -using System; +using System; using System.Collections.Generic; -using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Microsoft.Owin.Security.DataProtection; -using Umbraco.Core; 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 +namespace Umbraco.Core.BackOffice { public class BackOfficeUserManager : BackOfficeUserManager { - public const string OwinMarkerKey = "Umbraco.Web.Security.Identity.BackOfficeUserManagerMarker"; - - public BackOfficeUserManager( - IPasswordConfiguration passwordConfiguration, - IIpResolver ipResolver, - IUserStore store, - IOptions optionsAccessor, - IEnumerable> userValidators, - IEnumerable> passwordValidators, - ILookupNormalizer keyNormalizer, - IdentityErrorDescriber errors, - IDataProtectionProvider dataProtectionProvider, - ILogger> logger) - : base(passwordConfiguration, ipResolver, store, optionsAccessor, userValidators, passwordValidators, keyNormalizer, errors, null, logger) + public BackOfficeUserManager(IIpResolver ipResolver, IUserStore store, IOptions optionsAccessor, IPasswordHasher passwordHasher, IEnumerable> userValidators, IEnumerable> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger> logger) + : base(ipResolver, store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) { - InitUserManager(this, dataProtectionProvider); } - - #region Static Create methods - - /// - /// Creates a BackOfficeUserManager instance with all default options and the default BackOfficeUserManager - /// - public static BackOfficeUserManager Create( - IUserService userService, - IEntityService entityService, - IExternalLoginService externalLoginService, - IGlobalSettings globalSettings, - UmbracoMapper mapper, - IPasswordConfiguration passwordConfiguration, - IIpResolver ipResolver, - IdentityErrorDescriber errors, - IDataProtectionProvider dataProtectionProvider, - ILogger> logger) - { - var store = new BackOfficeUserStore(userService, entityService, externalLoginService, globalSettings, mapper); - - return Create( - passwordConfiguration, - ipResolver, - store, - errors, - dataProtectionProvider, - logger); - } - - /// - /// Creates a BackOfficeUserManager instance with all default options and a custom BackOfficeUserManager instance - /// - public static BackOfficeUserManager Create( - IPasswordConfiguration passwordConfiguration, - IIpResolver ipResolver, - IUserStore customUserStore, - IdentityErrorDescriber errors, - IDataProtectionProvider dataProtectionProvider, - ILogger> logger) - { - var options = new IdentityOptions(); - - // Configure validation logic for usernames - var userValidators = new List> { new BackOfficeUserValidator() }; - options.User.RequireUniqueEmail = true; - - // Configure validation logic for passwords - var passwordValidators = new List> { new PasswordValidator() }; - options.Password.RequiredLength = passwordConfiguration.RequiredLength; - options.Password.RequireNonAlphanumeric = passwordConfiguration.RequireNonLetterOrDigit; - options.Password.RequireDigit = passwordConfiguration.RequireDigit; - options.Password.RequireLowercase = passwordConfiguration.RequireLowercase; - options.Password.RequireUppercase = passwordConfiguration.RequireUppercase; - - // Ensure Umbraco security stamp claim type is used - options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier; - options.ClaimsIdentity.UserNameClaimType = ClaimTypes.Name; - options.ClaimsIdentity.RoleClaimType = ClaimTypes.Role; - options.ClaimsIdentity.SecurityStampClaimType = Constants.Web.SecurityStampClaimType; - - options.Lockout.AllowedForNewUsers = true; - options.Lockout.MaxFailedAccessAttempts = passwordConfiguration.MaxFailedAccessAttemptsBeforeLockout; - //NOTE: This just needs to be in the future, we currently don't support a lockout timespan, it's either they are locked - // or they are not locked, but this determines what is set on the account lockout date which corresponds to whether they are - // locked out or not. - options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromDays(30); - - return new BackOfficeUserManager( - passwordConfiguration, - ipResolver, - customUserStore, - new OptionsWrapper(options), - userValidators, - passwordValidators, - new NopLookupNormalizer(), - errors, - dataProtectionProvider, - logger); - } - - #endregion } public class BackOfficeUserManager : UserManager where T : BackOfficeIdentityUser { private PasswordGenerator _passwordGenerator; - + public BackOfficeUserManager( - IPasswordConfiguration passwordConfiguration, IIpResolver ipResolver, IUserStore store, IOptions optionsAccessor, + IPasswordHasher passwordHasher, IEnumerable> userValidators, IEnumerable> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger> logger) - : base(store, optionsAccessor, null, userValidators, passwordValidators, keyNormalizer, errors, services, logger) + : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) { - PasswordConfiguration = passwordConfiguration ?? throw new ArgumentNullException(nameof(passwordConfiguration)); IpResolver = ipResolver ?? throw new ArgumentNullException(nameof(ipResolver)); } @@ -157,33 +56,6 @@ namespace Umbraco.Web.Security public override bool SupportsUserPhoneNumber => false; #endregion - /// - /// Initializes the user manager with the correct options - /// - protected void InitUserManager( - BackOfficeUserManager manager, - IDataProtectionProvider dataProtectionProvider) - { - // use a custom hasher based on our membership provider - PasswordHasher = GetDefaultPasswordHasher(PasswordConfiguration); - - // set OWIN data protection token provider as default - if (dataProtectionProvider != null) - { - manager.RegisterTokenProvider( - TokenOptions.DefaultProvider, - new OwinDataProtectorTokenProvider(dataProtectionProvider.Create("ASP.NET Identity")) - { - TokenLifespan = TimeSpan.FromDays(3) - }); - } - - // register ASP.NET Core Identity token providers - manager.RegisterTokenProvider(TokenOptions.DefaultEmailProvider, new EmailTokenProvider()); - manager.RegisterTokenProvider(TokenOptions.DefaultPhoneProvider, new PhoneNumberTokenProvider()); - manager.RegisterTokenProvider(TokenOptions.DefaultAuthenticatorProvider, new AuthenticatorTokenProvider()); - } - /// /// Used to validate a user's session /// @@ -206,14 +78,14 @@ namespace Umbraco.Web.Security protected virtual IPasswordHasher GetDefaultPasswordHasher(IPasswordConfiguration passwordConfiguration) { //we can use the user aware password hasher (which will be the default and preferred way) - return new UserAwarePasswordHasher(new PasswordSecurity(passwordConfiguration)); + return new PasswordHasher(); } /// /// Gets/sets the default back office user password checker /// public IBackOfficeUserPasswordChecker BackOfficeUserPasswordChecker { get; set; } - public IPasswordConfiguration PasswordConfiguration { get; } + public IPasswordConfiguration PasswordConfiguration { get; protected set; } public IIpResolver IpResolver { get; } /// @@ -457,57 +329,57 @@ namespace Umbraco.Web.Security return result; } - internal void RaiseAccountLockedEvent(int userId) + public void RaiseAccountLockedEvent(int userId) { OnAccountLocked(new IdentityAuditEventArgs(AuditEvent.AccountLocked, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId)); } - internal void RaiseAccountUnlockedEvent(int userId) + public void RaiseAccountUnlockedEvent(int userId) { OnAccountUnlocked(new IdentityAuditEventArgs(AuditEvent.AccountUnlocked, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId)); } - internal void RaiseForgotPasswordRequestedEvent(int userId) + public void RaiseForgotPasswordRequestedEvent(int userId) { OnForgotPasswordRequested(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordRequested, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId)); } - internal void RaiseForgotPasswordChangedSuccessEvent(int userId) + public void RaiseForgotPasswordChangedSuccessEvent(int userId) { OnForgotPasswordChangedSuccess(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordChangedSuccess, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId)); } - internal void RaiseLoginFailedEvent(int userId) + public void RaiseLoginFailedEvent(int userId) { OnLoginFailed(new IdentityAuditEventArgs(AuditEvent.LoginFailed, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId)); } - internal void RaiseInvalidLoginAttemptEvent(string username) + public void RaiseInvalidLoginAttemptEvent(string username) { OnLoginFailed(new IdentityAuditEventArgs(AuditEvent.LoginFailed, IpResolver.GetCurrentRequestIpAddress(), username, string.Format("Attempted login for username '{0}' failed", username))); } - internal void RaiseLoginRequiresVerificationEvent(int userId) + public void RaiseLoginRequiresVerificationEvent(int userId) { OnLoginRequiresVerification(new IdentityAuditEventArgs(AuditEvent.LoginRequiresVerification, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId)); } - internal void RaiseLoginSuccessEvent(int userId) + public void RaiseLoginSuccessEvent(int userId) { OnLoginSuccess(new IdentityAuditEventArgs(AuditEvent.LoginSucces, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId)); } - internal void RaiseLogoutSuccessEvent(int userId) + public void RaiseLogoutSuccessEvent(int userId) { OnLogoutSuccess(new IdentityAuditEventArgs(AuditEvent.LogoutSuccess, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId)); } - internal void RaisePasswordChangedEvent(int userId) + public void RaisePasswordChangedEvent(int userId) { OnPasswordChanged(new IdentityAuditEventArgs(AuditEvent.PasswordChanged, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId)); } - internal void RaiseResetAccessFailedCountEvent(int userId) + public void RaiseResetAccessFailedCountEvent(int userId) { OnResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.ResetAccessFailedCount, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId)); } diff --git a/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserPasswordCheckerResult.cs b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserPasswordCheckerResult.cs new file mode 100644 index 0000000000..7936fab682 --- /dev/null +++ b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserPasswordCheckerResult.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Core.BackOffice +{ + /// + /// The result returned from the IBackOfficeUserPasswordChecker + /// + public enum BackOfficeUserPasswordCheckerResult + { + ValidCredentials, + InvalidCredentials, + FallbackToDefaultChecker + } +} diff --git a/src/Umbraco.Web/Security/BackOfficeUserStore.cs b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs similarity index 99% rename from src/Umbraco.Web/Security/BackOfficeUserStore.cs rename to src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs index 6f401163aa..b75735e688 100644 --- a/src/Umbraco.Web/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs @@ -5,17 +5,14 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; -using Umbraco.Core; 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 +namespace Umbraco.Core.BackOffice { public class BackOfficeUserStore : DisposableObjectSlim, IUserPasswordStore, diff --git a/src/Umbraco.Web/Security/BackOfficeUserValidator.cs b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserValidator.cs similarity index 90% rename from src/Umbraco.Web/Security/BackOfficeUserValidator.cs rename to src/Umbraco.Infrastructure/BackOffice/BackOfficeUserValidator.cs index 94e9c2e0bd..131bd08ac9 100644 --- a/src/Umbraco.Web/Security/BackOfficeUserValidator.cs +++ b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserValidator.cs @@ -1,8 +1,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; -using Umbraco.Web.Models.Identity; -namespace Umbraco.Web.Security +namespace Umbraco.Core.BackOffice { public class BackOfficeUserValidator : UserValidator where T : BackOfficeIdentityUser diff --git a/src/Umbraco.Infrastructure/BackOffice/Extensions/ClaimsPrincipalExtensions.cs b/src/Umbraco.Infrastructure/BackOffice/Extensions/ClaimsPrincipalExtensions.cs new file mode 100644 index 0000000000..a7eea63983 --- /dev/null +++ b/src/Umbraco.Infrastructure/BackOffice/Extensions/ClaimsPrincipalExtensions.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using Umbraco.Core; +using Umbraco.Core.BackOffice; + +namespace Umbraco.Extensions +{ + public static class ClaimsPrincipalExtensions + { + /// + /// 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; + } + } +} diff --git a/src/Umbraco.Infrastructure/BackOffice/Extensions/IdentityExtensions.cs b/src/Umbraco.Infrastructure/BackOffice/Extensions/IdentityExtensions.cs new file mode 100644 index 0000000000..95a63c6001 --- /dev/null +++ b/src/Umbraco.Infrastructure/BackOffice/Extensions/IdentityExtensions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Identity; + +namespace Umbraco.Extensions +{ + public static class IdentityExtensions + { + public static string ToErrorMessage(this IEnumerable errors) + { + if (errors == null) throw new ArgumentNullException(nameof(errors)); + return string.Join(", ", errors.Select(x => x.Description).ToList()); + } + } +} diff --git a/src/Umbraco.Infrastructure/BackOffice/IBackOfficeUserPasswordChecker.cs b/src/Umbraco.Infrastructure/BackOffice/IBackOfficeUserPasswordChecker.cs new file mode 100644 index 0000000000..5874337f4a --- /dev/null +++ b/src/Umbraco.Infrastructure/BackOffice/IBackOfficeUserPasswordChecker.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; + +namespace Umbraco.Core.BackOffice +{ + /// + /// Used by the BackOfficeUserManager to check the username/password which allows for developers to more easily + /// set the logic for this procedure. + /// + public interface IBackOfficeUserPasswordChecker + { + /// + /// Checks a password for a user + /// + /// + /// + /// + /// + /// This will allow a developer to auto-link a local account which is required if the user queried doesn't exist locally. + /// The user parameter will always contain the username, if the user doesn't exist locally, the other properties will not be filled in. + /// A developer can then create a local account by filling in the properties and using UserManager.CreateAsync + /// + Task CheckPasswordAsync(BackOfficeIdentityUser user, string password); + } +} diff --git a/src/Umbraco.Web/Security/IUserSessionStore.cs b/src/Umbraco.Infrastructure/BackOffice/IUserSessionStore.cs similarity index 92% rename from src/Umbraco.Web/Security/IUserSessionStore.cs rename to src/Umbraco.Infrastructure/BackOffice/IUserSessionStore.cs index 06b7c2f165..69d5408cf7 100644 --- a/src/Umbraco.Web/Security/IUserSessionStore.cs +++ b/src/Umbraco.Infrastructure/BackOffice/IUserSessionStore.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; -namespace Umbraco.Core.Security +namespace Umbraco.Core.BackOffice { /// /// An IUserStore interface part to implement if the store supports validating user session Ids diff --git a/src/Umbraco.Infrastructure/BackOffice/IdentityAuditEventArgs.cs b/src/Umbraco.Infrastructure/BackOffice/IdentityAuditEventArgs.cs new file mode 100644 index 0000000000..1991f248f1 --- /dev/null +++ b/src/Umbraco.Infrastructure/BackOffice/IdentityAuditEventArgs.cs @@ -0,0 +1,132 @@ +using System; +using System.Threading; +using Umbraco.Extensions; + + +namespace Umbraco.Core.BackOffice +{ + /// + /// This class is used by events raised from the BackofficeUserManager + /// + public class IdentityAuditEventArgs : EventArgs + { + /// + /// The action that got triggered from the audit event + /// + public AuditEvent Action { get; private set; } + + /// + /// Current date/time in UTC format + /// + public DateTime DateTimeUtc { get; private set; } + + /// + /// The source IP address of the user performing the action + /// + public string IpAddress { get; private set; } + + /// + /// The user affected by the event raised + /// + public int AffectedUser { get; private set; } + + /// + /// If a user is performing an action on a different user, then this will be set. Otherwise it will be -1 + /// + public int PerformingUser { get; private set; } + + /// + /// An optional comment about the action being logged + /// + public string Comment { get; private set; } + + /// + /// This property is always empty except in the LoginFailed event for an unknown user trying to login + /// + public string Username { get; private set; } + + + /// + /// Default constructor + /// + /// + /// + /// + /// + /// + public IdentityAuditEventArgs(AuditEvent action, string ipAddress, string comment = null, int performingUser = -1, int affectedUser = -1) + { + DateTimeUtc = DateTime.UtcNow; + Action = action; + + IpAddress = ipAddress; + Comment = comment; + AffectedUser = affectedUser; + + PerformingUser = performingUser == -1 + ? GetCurrentRequestBackofficeUserId() + : performingUser; + } + + /// + /// Creates an instance without a performing or affected user (the id will be set to -1) + /// + /// + /// + /// + /// + public IdentityAuditEventArgs(AuditEvent action, string ipAddress, string username, string comment) + { + DateTimeUtc = DateTime.UtcNow; + Action = action; + + IpAddress = ipAddress; + Username = username; + Comment = comment; + + PerformingUser = -1; + } + + public IdentityAuditEventArgs(AuditEvent action, string ipAddress, string username, string comment, int performingUser) + { + DateTimeUtc = DateTime.UtcNow; + Action = action; + + IpAddress = ipAddress; + Username = username; + Comment = comment; + + PerformingUser = performingUser == -1 + ? GetCurrentRequestBackofficeUserId() + : performingUser; + } + + /// + /// Returns the current logged in backoffice user's Id logging if there is one + /// + /// + protected int GetCurrentRequestBackofficeUserId() + { + var userId = -1; + /*var backOfficeIdentity = Thread.CurrentPrincipal.GetUmbracoIdentity(); + if (backOfficeIdentity != null) + int.TryParse(backOfficeIdentity.Id.ToString(), out userId);*/ + return userId; + } + } + + public enum AuditEvent + { + AccountLocked, + AccountUnlocked, + ForgotPasswordRequested, + ForgotPasswordChangedSuccess, + LoginFailed, + LoginRequiresVerification, + LoginSucces, + LogoutSuccess, + PasswordChanged, + PasswordReset, + ResetAccessFailedCount + } +} diff --git a/src/Umbraco.Web/Models/Identity/IdentityMapDefinition.cs b/src/Umbraco.Infrastructure/BackOffice/IdentityMapDefinition.cs similarity index 98% rename from src/Umbraco.Web/Models/Identity/IdentityMapDefinition.cs rename to src/Umbraco.Infrastructure/BackOffice/IdentityMapDefinition.cs index 7c20c6108a..59590907f6 100644 --- a/src/Umbraco.Web/Models/Identity/IdentityMapDefinition.cs +++ b/src/Umbraco.Infrastructure/BackOffice/IdentityMapDefinition.cs @@ -1,12 +1,11 @@ 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 +namespace Umbraco.Core.BackOffice { public class IdentityMapDefinition : IMapDefinition { diff --git a/src/Umbraco.Web/Security/NopLookupNormalizer.cs b/src/Umbraco.Infrastructure/BackOffice/NopLookupNormalizer.cs similarity index 91% rename from src/Umbraco.Web/Security/NopLookupNormalizer.cs rename to src/Umbraco.Infrastructure/BackOffice/NopLookupNormalizer.cs index 08aa8d548a..6797d62e6c 100644 --- a/src/Umbraco.Web/Security/NopLookupNormalizer.cs +++ b/src/Umbraco.Infrastructure/BackOffice/NopLookupNormalizer.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Identity; -namespace Umbraco.Web.Security +namespace Umbraco.Core.BackOffice { /// /// No-op lookup normalizer to maintain compatibility with ASP.NET Identity 2 diff --git a/src/Umbraco.Web/Security/UmbracoBackOfficeIdentity.cs b/src/Umbraco.Infrastructure/BackOffice/UmbracoBackOfficeIdentity.cs similarity index 99% rename from src/Umbraco.Web/Security/UmbracoBackOfficeIdentity.cs rename to src/Umbraco.Infrastructure/BackOffice/UmbracoBackOfficeIdentity.cs index c3697d5e9e..c1ec692339 100644 --- a/src/Umbraco.Web/Security/UmbracoBackOfficeIdentity.cs +++ b/src/Umbraco.Infrastructure/BackOffice/UmbracoBackOfficeIdentity.cs @@ -2,9 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Security.Claims; -using Umbraco.Web; -namespace Umbraco.Core.Security +namespace Umbraco.Core.BackOffice { /// diff --git a/src/Umbraco.Web/Models/Identity/UserLoginInfoWrapper.cs b/src/Umbraco.Infrastructure/BackOffice/UserLoginInfoWrapper.cs similarity index 86% rename from src/Umbraco.Web/Models/Identity/UserLoginInfoWrapper.cs rename to src/Umbraco.Infrastructure/BackOffice/UserLoginInfoWrapper.cs index 336b4c9e72..a441d0299a 100644 --- a/src/Umbraco.Web/Models/Identity/UserLoginInfoWrapper.cs +++ b/src/Umbraco.Infrastructure/BackOffice/UserLoginInfoWrapper.cs @@ -1,9 +1,9 @@ using Microsoft.AspNetCore.Identity; using Umbraco.Core.Models.Identity; -namespace Umbraco.Web.Models.Identity +namespace Umbraco.Core.BackOffice { - internal class UserLoginInfoWrapper : IUserLoginInfo + public class UserLoginInfoWrapper : IUserLoginInfo { private readonly UserLoginInfo _info; diff --git a/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs index 3ae92d0179..a240eaf104 100644 --- a/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Specialized; -using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; @@ -10,8 +9,9 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Services; using Umbraco.Web.Install.Models; +using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Web.Security; +using Umbraco.Extensions; namespace Umbraco.Web.Install.InstallSteps { @@ -34,8 +34,9 @@ namespace Umbraco.Web.Install.InstallSteps private readonly ISecuritySettings _securitySettings; private readonly IConnectionStrings _connectionStrings; private readonly ICookieManager _cookieManager; + private readonly BackOfficeUserManager _userManager; - public NewInstallStep(IUserService userService, DatabaseBuilder databaseBuilder, IGlobalSettings globalSettings, IUserPasswordConfiguration passwordConfiguration, ISecuritySettings securitySettings, IConnectionStrings connectionStrings, ICookieManager cookieManager) + public NewInstallStep(IUserService userService, DatabaseBuilder databaseBuilder, IGlobalSettings globalSettings, IUserPasswordConfiguration passwordConfiguration, ISecuritySettings securitySettings, IConnectionStrings connectionStrings, ICookieManager cookieManager, BackOfficeUserManager userManager) { _userService = userService ?? throw new ArgumentNullException(nameof(userService)); _databaseBuilder = databaseBuilder ?? throw new ArgumentNullException(nameof(databaseBuilder)); @@ -44,6 +45,7 @@ namespace Umbraco.Web.Install.InstallSteps _securitySettings = securitySettings ?? throw new ArgumentNullException(nameof(securitySettings)); _connectionStrings = connectionStrings ?? throw new ArgumentNullException(nameof(connectionStrings)); _cookieManager = cookieManager; + _userManager = userManager ?? throw new ArgumentNullException(nameof(userManager)); } public override async Task ExecuteAsync(UserModel user) @@ -59,26 +61,21 @@ namespace Umbraco.Web.Install.InstallSteps _userService.Save(admin); - //TODO: This needs to be reintroduced, when users are compatible with ASP.NET Core Identity. - // var userManager = _httpContextAccessor.GetRequiredHttpContext().GetOwinContext().GetBackOfficeUserManager(); - // var membershipUser = await userManager.FindByIdAsync(Constants.Security.SuperUserId.ToString()); - // if (membershipUser == null) - // { - // throw new InvalidOperationException( - // $"No user found in membership provider with id of {Constants.Security.SuperUserId}."); - // } - // - // //To change the password here we actually need to reset it since we don't have an old one to use to change - // var resetToken = await userManager.GeneratePasswordResetTokenAsync(membershipUser); - // if (string.IsNullOrWhiteSpace(resetToken)) - // throw new InvalidOperationException("Could not reset password: unable to generate internal reset token"); - // - // var resetResult = await userManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, user.Password.Trim()); - // if (!resetResult.Succeeded) - // throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors.ToErrorMessage())); - + var membershipUser = await _userManager.FindByIdAsync(Constants.Security.SuperUserId.ToString()); + if (membershipUser == null) + { + throw new InvalidOperationException( + $"No user found in membership provider with id of {Constants.Security.SuperUserId}."); + } + //To change the password here we actually need to reset it since we don't have an old one to use to change + var resetToken = await _userManager.GeneratePasswordResetTokenAsync(membershipUser); + if (string.IsNullOrWhiteSpace(resetToken)) + throw new InvalidOperationException("Could not reset password: unable to generate internal reset token"); + var resetResult = await _userManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, user.Password.Trim()); + if (!resetResult.Succeeded) + throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors.ToErrorMessage())); if (user.SubscribeToNewsLetter) { diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 7948c3ea31..1af8ce9119 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -17,6 +17,7 @@ + diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index a38e002810..8f9ec4d833 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -109,6 +109,8 @@ namespace Umbraco.Tests.Integration.Testing // Add it! services.AddUmbracoConfiguration(hostContext.Configuration); services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, NoAppCache.Instance, testHelper.GetLoggingConfiguration(), out _); + + CustomTestSetup(services); }); var host = await hostBuilder.StartAsync(); @@ -123,6 +125,8 @@ namespace Umbraco.Tests.Integration.Testing #region Common services + protected virtual Action CustomTestSetup => services => { }; + /// /// Returns the DI container /// 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 new file mode 100644 index 0000000000..51fce283f8 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UmbracoBackOfficeServiceCollectionExtensionsTests.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using Umbraco.Extensions; +using Umbraco.Core.BackOffice; +using Umbraco.Tests.Integration.Testing; + +namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Extensions +{ + [TestFixture] + public class UmbracoBackOfficeServiceCollectionExtensionsTests : UmbracoIntegrationTest + { + [Test] + public void AddUmbracoBackOfficeIdentity_ExpectBackOfficeUserStoreResolvable() + { + var userStore = Services.GetService>(); + + Assert.IsNotNull(userStore); + 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() + { + var userManager = Services.GetService(); + + Assert.NotNull(userManager); + } + + protected override Action CustomTestSetup => (services) => services.AddUmbracoBackOfficeIdentity(); + } +} 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/Security/AuthenticationExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/IdentityExtensionsTests.cs similarity index 94% rename from src/Umbraco.Tests/Security/AuthenticationExtensionsTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/IdentityExtensionsTests.cs index e622458c39..baf4a6f062 100644 --- a/src/Umbraco.Tests/Security/AuthenticationExtensionsTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/IdentityExtensionsTests.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Identity; using NUnit.Framework; -using Umbraco.Web.Security; +using Umbraco.Extensions; namespace Umbraco.Tests.Security { - public class AuthenticationExtensionsTests + public class IdentityExtensionsTests { [Test] public void ToErrorMessage_When_Errors_Are_Null_Expect_ArgumentNullException() 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.UnitTests/Umbraco.Tests.UnitTests.csproj b/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj index 56a817f8cb..3176f3d36f 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj @@ -6,18 +6,6 @@ false - - - - - - - - - - - - diff --git a/src/Umbraco.Tests/Security/BackOfficeUserManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeOwinUserManagerTests.cs similarity index 94% rename from src/Umbraco.Tests/Security/BackOfficeUserManagerTests.cs rename to src/Umbraco.Tests/Security/BackOfficeOwinUserManagerTests.cs index 30ed101297..705387b3ae 100644 --- a/src/Umbraco.Tests/Security/BackOfficeUserManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeOwinUserManagerTests.cs @@ -6,15 +6,15 @@ 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; namespace Umbraco.Tests.Security { - public class BackOfficeUserManagerTests + public class BackOfficeOwinUserManagerTests { [Test] public async Task CheckPasswordAsync_When_Default_Password_Hasher_Validates_Umbraco7_Hash_Expect_Valid_Password() @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Security mockPasswordConfiguration.Setup(x => x.HashAlgorithmType) .Returns("HMACSHA256"); - var userManager = BackOfficeUserManager.Create( + var userManager = BackOfficeOwinUserManager.Create( mockPasswordConfiguration.Object, mockIpResolver.Object, mockUserStore.Object, 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..b80e526037 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( 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( 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( 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((BackOfficeOwinUserManager) 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( 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( 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( 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( 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( 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> regenFunc = (signInManager, userManager, user) => { regenFuncCalled = true; @@ -201,7 +202,7 @@ namespace Umbraco.Tests.Security }; var func = UmbracoSecurityStampValidator - .OnValidateIdentity, BackOfficeIdentityUser>( + .OnValidateIdentity( 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..3ca52b73f1 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -146,11 +146,8 @@ - - - - + @@ -275,7 +272,6 @@ - diff --git a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs index 31d1f25c1d..1158d00b4e 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 { @@ -474,7 +473,7 @@ namespace Umbraco.Tests.Web.Controllers mockUserManager.Verify(); } - private UsersController CreateSut(IMock> mockUserManager = null) + private UsersController CreateSut(IMock mockUserManager = null) { var mockLocalizedTextService = new Mock(); mockLocalizedTextService.Setup(x => x.Localize(It.IsAny(), It.IsAny(), It.IsAny>())) @@ -515,9 +514,9 @@ namespace Umbraco.Tests.Web.Controllers return usersController; } - private static Mock> CreateMockUserManager() + private static Mock CreateMockUserManager() { - return new Mock>( + return new Mock( new Mock().Object, new Mock().Object, new Mock>().Object, diff --git a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs new file mode 100644 index 0000000000..f3a9a528ae --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -0,0 +1,57 @@ +using System; +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; +using Umbraco.Net; +using Umbraco.Web.Common.AspNetCore; + +namespace Umbraco.Extensions +{ + public static class UmbracoBackOfficeServiceCollectionExtensions + { + public static void AddUmbracoBackOfficeIdentity(this IServiceCollection services) + { + services.AddDataProtection(); + + // UmbracoMapper - hack? + services.TryAddSingleton(); + services.TryAddSingleton(s => new MapDefinitionCollection(new[] {s.GetService()})); + services.TryAddSingleton(); + + services.TryAddScoped(); + + services.AddIdentityCore(options => + { + options.User.RequireUniqueEmail = true; + + // TODO: Configure password configuration + /*options.Password.RequiredLength = passwordConfiguration.RequiredLength; + options.Password.RequireNonAlphanumeric = passwordConfiguration.RequireNonLetterOrDigit; + options.Password.RequireDigit = passwordConfiguration.RequireDigit; + options.Password.RequireLowercase = passwordConfiguration.RequireLowercase; + options.Password.RequireUppercase = passwordConfiguration.RequireUppercase; + options.Lockout.MaxFailedAccessAttempts = passwordConfiguration.MaxFailedAccessAttemptsBeforeLockout;*/ + + options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier; + options.ClaimsIdentity.UserNameClaimType = ClaimTypes.Name; + options.ClaimsIdentity.RoleClaimType = ClaimTypes.Role; + options.ClaimsIdentity.SecurityStampClaimType = Constants.Web.SecurityStampClaimType; + + options.Lockout.AllowedForNewUsers = true; + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromDays(30); + }) + .AddDefaultTokenProviders() + .AddUserStore() + .AddUserManager() + .AddClaimsPrincipalFactory>(); + + services.AddScoped(); + services.TryAddScoped>(); + + } + } +} diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index 95eaab851b..547a320de1 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -43,6 +43,7 @@ namespace Umbraco.Web.UI.BackOffice services.AddUmbracoCore(_env, out var factory); services.AddUmbracoWebComponents(); services.AddUmbracoRuntimeMinifier(_config); + services.AddUmbracoBackOfficeIdentity(); services.AddMvc(); 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/Compose/BackOfficeUserAuditEventsComponent.cs b/src/Umbraco.Web/Compose/BackOfficeUserAuditEventsComponent.cs index 84fb0e6bb8..dcb5fac32d 100644 --- a/src/Umbraco.Web/Compose/BackOfficeUserAuditEventsComponent.cs +++ b/src/Umbraco.Web/Compose/BackOfficeUserAuditEventsComponent.cs @@ -26,14 +26,14 @@ namespace Umbraco.Web.Compose { //BackOfficeUserManager.AccountLocked += ; //BackOfficeUserManager.AccountUnlocked += ; - BackOfficeUserManager.ForgotPasswordRequested += OnForgotPasswordRequest; - BackOfficeUserManager.ForgotPasswordChangedSuccess += OnForgotPasswordChange; - BackOfficeUserManager.LoginFailed += OnLoginFailed; + BackOfficeOwinUserManager.ForgotPasswordRequested += OnForgotPasswordRequest; + BackOfficeOwinUserManager.ForgotPasswordChangedSuccess += OnForgotPasswordChange; + BackOfficeOwinUserManager.LoginFailed += OnLoginFailed; //BackOfficeUserManager.LoginRequiresVerification += ; - BackOfficeUserManager.LoginSuccess += OnLoginSuccess; - BackOfficeUserManager.LogoutSuccess += OnLogoutSuccess; - BackOfficeUserManager.PasswordChanged += OnPasswordChanged; - BackOfficeUserManager.PasswordReset += OnPasswordReset; + BackOfficeOwinUserManager.LoginSuccess += OnLoginSuccess; + BackOfficeOwinUserManager.LogoutSuccess += OnLogoutSuccess; + BackOfficeOwinUserManager.PasswordChanged += OnPasswordChanged; + BackOfficeOwinUserManager.PasswordReset += OnPasswordReset; //BackOfficeUserManager.ResetAccessFailedCount += ; } 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 063bc89269..81d714926f 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -25,10 +25,9 @@ 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.Extensions; using Umbraco.Web.Routing; namespace Umbraco.Web.Editors @@ -42,7 +41,7 @@ namespace Umbraco.Web.Editors [IsBackOffice] public class AuthenticationController : UmbracoApiController { - private BackOfficeUserManager _userManager; + private BackOfficeOwinUserManager _userManager; private BackOfficeSignInManager _signInManager; private readonly IUserPasswordConfiguration _passwordConfiguration; private readonly IHostingEnvironment _hostingEnvironment; @@ -76,8 +75,8 @@ namespace Umbraco.Web.Editors _emailSender = emailSender; } - protected BackOfficeUserManager UserManager => _userManager - ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager()); + protected BackOfficeOwinUserManager 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 5b3a01052d..4e65d0200d 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -18,15 +18,16 @@ 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 Umbraco.Core.Configuration.Grid; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Hosting; using Umbraco.Core.WebAssets; +using Umbraco.Extensions; using Umbraco.Web.Trees; using Umbraco.Web.WebAssets; +using BackOfficeIdentityUser = Umbraco.Core.BackOffice.BackOfficeIdentityUser; namespace Umbraco.Web.Editors { @@ -40,7 +41,7 @@ namespace Umbraco.Web.Editors { private readonly UmbracoFeatures _features; private readonly IRuntimeState _runtimeState; - private BackOfficeUserManager _userManager; + private BackOfficeOwinUserManager _userManager; private BackOfficeSignInManager _signInManager; private readonly IUmbracoVersion _umbracoVersion; private readonly IGridConfig _gridConfig; @@ -85,7 +86,7 @@ namespace Umbraco.Web.Editors protected BackOfficeSignInManager SignInManager => _signInManager ?? (_signInManager = OwinContext.GetBackOfficeSignInManager()); - protected BackOfficeUserManager UserManager => _userManager ?? (_userManager = OwinContext.GetBackOfficeUserManager()); + protected BackOfficeOwinUserManager UserManager => _userManager ?? (_userManager = OwinContext.GetBackOfficeUserManager()); protected IAuthenticationManager AuthenticationManager => OwinContext.Authentication; diff --git a/src/Umbraco.Web/Editors/CurrentUserController.cs b/src/Umbraco.Web/Editors/CurrentUserController.cs index 2ad09dc895..2bfbcb0c22 100644 --- a/src/Umbraco.Web/Editors/CurrentUserController.cs +++ b/src/Umbraco.Web/Editors/CurrentUserController.cs @@ -25,6 +25,7 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Hosting; using Umbraco.Web.Routing; using Umbraco.Core.Media; +using Umbraco.Extensions; namespace Umbraco.Web.Editors { diff --git a/src/Umbraco.Web/Editors/PasswordChanger.cs b/src/Umbraco.Web/Editors/PasswordChanger.cs index b6757f76cc..4b3995ef06 100644 --- a/src/Umbraco.Web/Editors/PasswordChanger.cs +++ b/src/Umbraco.Web/Editors/PasswordChanger.cs @@ -4,10 +4,8 @@ using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.Identity; -using Umbraco.Core.Security; +using Umbraco.Extensions; 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) + BackOfficeOwinUserManager 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 9a35990be7..073929b7a7 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -12,6 +12,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; @@ -25,7 +26,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; @@ -37,7 +37,7 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Hosting; using Umbraco.Web.Routing; using Umbraco.Core.Media; -using Umbraco.Web.Security; +using Umbraco.Extensions; namespace Umbraco.Web.Editors { diff --git a/src/Umbraco.Web/OwinExtensions.cs b/src/Umbraco.Web/OwinExtensions.cs index 52c1187707..4ea4040ec6 100644 --- a/src/Umbraco.Web/OwinExtensions.cs +++ b/src/Umbraco.Web/OwinExtensions.cs @@ -3,7 +3,6 @@ using System.Web; using Microsoft.Owin; using Microsoft.Owin.Security; using Umbraco.Core; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; namespace Umbraco.Web @@ -72,13 +71,13 @@ namespace Umbraco.Web /// This is required because to extract the user manager we need to user a custom service since owin only deals in generics and /// developers could register their own user manager types /// - public static BackOfficeUserManager GetBackOfficeUserManager(this IOwinContext owinContext) + public static BackOfficeOwinUserManager GetBackOfficeUserManager(this IOwinContext owinContext) { - var marker = owinContext.Get(BackOfficeUserManager.OwinMarkerKey) + var marker = owinContext.Get(BackOfficeOwinUserManager.OwinMarkerKey) ?? throw new NullReferenceException($"No {typeof (IBackOfficeUserManagerMarker)}, i.e. no Umbraco back-office, has been registered with Owin."); return marker.GetManager(owinContext) - ?? throw new NullReferenceException($"Could not resolve an instance of {typeof (BackOfficeUserManager)} from the {typeof (IOwinContext)}."); + ?? throw new NullReferenceException($"Could not resolve an instance of {typeof (BackOfficeOwinUserManager)} from the {typeof (IOwinContext)}."); } /// 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..81c5408e98 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 @@ -43,8 +43,8 @@ namespace Umbraco.Web.Security if (services == null) throw new ArgumentNullException(nameof(services)); //Configure Umbraco user manager to be created per request - app.CreatePerOwinContext( - (options, owinContext) => BackOfficeUserManager.Create( + app.CreatePerOwinContext( + (options, owinContext) => BackOfficeOwinUserManager.Create( services.UserService, services.EntityService, services.ExternalLoginService, @@ -56,7 +56,7 @@ namespace Umbraco.Web.Security app.GetDataProtectionProvider(), new NullLogger>())); - app.SetBackOfficeUserManagerType(); + app.SetBackOfficeUserManagerType(); //Create a sign in manager per request app.CreatePerOwinContext((options, context) => BackOfficeSignInManager.Create(context, globalSettings, app.CreateLogger())); @@ -77,8 +77,8 @@ namespace Umbraco.Web.Security if (customUserStore == null) throw new ArgumentNullException(nameof(customUserStore)); //Configure Umbraco user manager to be created per request - app.CreatePerOwinContext( - (options, owinContext) => BackOfficeUserManager.Create( + app.CreatePerOwinContext( + (options, owinContext) => BackOfficeOwinUserManager.Create( passwordConfiguration, ipResolver, customUserStore, @@ -86,7 +86,7 @@ namespace Umbraco.Web.Security app.GetDataProtectionProvider(), new NullLogger>())); - app.SetBackOfficeUserManagerType(); + app.SetBackOfficeUserManagerType(); //Create a sign in manager per request app.CreatePerOwinContext((options, context) => BackOfficeSignInManager.Create(context, globalSettings, app.CreateLogger(typeof(BackOfficeSignInManager).FullName))); @@ -153,7 +153,7 @@ namespace Umbraco.Web.Security // logs in. This is a security feature which is used when you // change a password or add an external login to your account. OnValidateIdentity = UmbracoSecurityStampValidator - .OnValidateIdentity( + .OnValidateIdentity( TimeSpan.FromMinutes(30), (signInManager, manager, user) => signInManager.CreateUserIdentityAsync(user), identity => identity.GetUserId()), @@ -240,7 +240,7 @@ namespace Umbraco.Web.Security // a generic strongly typed instance app.Use((context, func) => { - context.Set(BackOfficeUserManager.OwinMarkerKey, new BackOfficeUserManagerMarker()); + context.Set(BackOfficeOwinUserManager.OwinMarkerKey, new BackOfficeUserManagerMarker()); return func(); }); } diff --git a/src/Umbraco.Web/Security/AuthenticationExtensions.cs b/src/Umbraco.Web/Security/AuthenticationExtensions.cs index 7d76eaa7be..deb735ca56 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 /// @@ -383,12 +352,5 @@ namespace Umbraco.Web.Security /// Used so that we aren't creating a new CultureInfo object for every single request /// private static readonly ConcurrentDictionary UserCultures = new ConcurrentDictionary(); - - public static string ToErrorMessage(this IEnumerable errors) - { - if (errors == null) throw new ArgumentNullException(nameof(errors)); - return string.Join(", ", errors.Select(x => x.Description).ToList()); - } - } } 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/BackOfficeOwinUserManager.cs b/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs new file mode 100644 index 0000000000..005ae972c3 --- /dev/null +++ b/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Security.Claims; +using Microsoft.AspNetCore.Identity; +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; + +namespace Umbraco.Web.Security +{ + public class BackOfficeOwinUserManager : BackOfficeUserManager + { + public const string OwinMarkerKey = "Umbraco.Web.Security.Identity.BackOfficeUserManagerMarker"; + + public BackOfficeOwinUserManager( + IPasswordConfiguration passwordConfiguration, + IIpResolver ipResolver, + IUserStore store, + IOptions optionsAccessor, + IEnumerable> userValidators, + IEnumerable> passwordValidators, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + IDataProtectionProvider dataProtectionProvider, + ILogger> logger) + : base(ipResolver, store, optionsAccessor, null, userValidators, passwordValidators, keyNormalizer, errors, null, logger) + { + PasswordConfiguration = passwordConfiguration; + InitUserManager(this, dataProtectionProvider); + } + + #region Static Create methods + + /// + /// Creates a BackOfficeUserManager instance with all default options and the default BackOfficeUserManager + /// + public static BackOfficeOwinUserManager Create( + IUserService userService, + IEntityService entityService, + IExternalLoginService externalLoginService, + IGlobalSettings globalSettings, + UmbracoMapper mapper, + IPasswordConfiguration passwordConfiguration, + IIpResolver ipResolver, + IdentityErrorDescriber errors, + IDataProtectionProvider dataProtectionProvider, + ILogger> logger) + { + var store = new BackOfficeUserStore(userService, entityService, externalLoginService, globalSettings, mapper); + + return Create( + passwordConfiguration, + ipResolver, + store, + errors, + dataProtectionProvider, + logger); + } + + /// + /// Creates a BackOfficeUserManager instance with all default options and a custom BackOfficeUserManager instance + /// + public static BackOfficeOwinUserManager Create( + IPasswordConfiguration passwordConfiguration, + IIpResolver ipResolver, + IUserStore customUserStore, + IdentityErrorDescriber errors, + IDataProtectionProvider dataProtectionProvider, + ILogger> logger) + { + var options = new IdentityOptions(); + + // Configure validation logic for usernames + var userValidators = new List> { new BackOfficeUserValidator() }; + options.User.RequireUniqueEmail = true; + + // Configure validation logic for passwords + var passwordValidators = new List> { new PasswordValidator() }; + options.Password.RequiredLength = passwordConfiguration.RequiredLength; + options.Password.RequireNonAlphanumeric = passwordConfiguration.RequireNonLetterOrDigit; + options.Password.RequireDigit = passwordConfiguration.RequireDigit; + options.Password.RequireLowercase = passwordConfiguration.RequireLowercase; + options.Password.RequireUppercase = passwordConfiguration.RequireUppercase; + + // Ensure Umbraco security stamp claim type is used + options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier; + options.ClaimsIdentity.UserNameClaimType = ClaimTypes.Name; + options.ClaimsIdentity.RoleClaimType = ClaimTypes.Role; + options.ClaimsIdentity.SecurityStampClaimType = Constants.Web.SecurityStampClaimType; + + options.Lockout.AllowedForNewUsers = true; + options.Lockout.MaxFailedAccessAttempts = passwordConfiguration.MaxFailedAccessAttemptsBeforeLockout; + //NOTE: This just needs to be in the future, we currently don't support a lockout timespan, it's either they are locked + // or they are not locked, but this determines what is set on the account lockout date which corresponds to whether they are + // locked out or not. + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromDays(30); + + return new BackOfficeOwinUserManager( + passwordConfiguration, + ipResolver, + customUserStore, + new OptionsWrapper(options), + userValidators, + passwordValidators, + new NopLookupNormalizer(), + errors, + dataProtectionProvider, + logger); + } + + #endregion + + protected override IPasswordHasher GetDefaultPasswordHasher(IPasswordConfiguration passwordConfiguration) + { + return new UserAwarePasswordHasher(new PasswordSecurity(passwordConfiguration)); + } + + protected void InitUserManager(BackOfficeOwinUserManager manager, IDataProtectionProvider dataProtectionProvider) + { + // use a custom hasher based on our membership provider + PasswordHasher = GetDefaultPasswordHasher(PasswordConfiguration); + + // set OWIN data protection token provider as default + if (dataProtectionProvider != null) + { + manager.RegisterTokenProvider( + TokenOptions.DefaultProvider, + new OwinDataProtectorTokenProvider(dataProtectionProvider.Create("ASP.NET Identity")) + { + TokenLifespan = TimeSpan.FromDays(3) + }); + } + + // register ASP.NET Core Identity token providers + manager.RegisterTokenProvider(TokenOptions.DefaultEmailProvider, new EmailTokenProvider()); + manager.RegisterTokenProvider(TokenOptions.DefaultPhoneProvider, new PhoneNumberTokenProvider()); + manager.RegisterTokenProvider(TokenOptions.DefaultAuthenticatorProvider, new AuthenticatorTokenProvider()); + } + } +} 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/BackOfficeUserManagerMarker.cs b/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs index b3eb9da3d5..dd657b48bf 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 { @@ -15,9 +15,9 @@ namespace Umbraco.Web.Security where TManager : BackOfficeUserManager where TUser : BackOfficeIdentityUser { - public BackOfficeUserManager GetManager(IOwinContext owin) + public BackOfficeOwinUserManager GetManager(IOwinContext owin) { - var mgr = owin.Get() as BackOfficeUserManager; + var mgr = owin.Get() as BackOfficeOwinUserManager; if (mgr == null) throw new InvalidOperationException("Could not cast the registered back office user of type " + typeof(TManager) + " to " + typeof(BackOfficeUserManager)); return mgr; } 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..16c0666c9c 100644 --- a/src/Umbraco.Web/Security/IBackOfficeUserManagerMarker.cs +++ b/src/Umbraco.Web/Security/IBackOfficeUserManagerMarker.cs @@ -1,5 +1,4 @@ using Microsoft.Owin; -using Umbraco.Web.Models.Identity; namespace Umbraco.Web.Security { @@ -10,6 +9,6 @@ namespace Umbraco.Web.Security /// internal interface IBackOfficeUserManagerMarker { - BackOfficeUserManager GetManager(IOwinContext owin); + BackOfficeOwinUserManager GetManager(IOwinContext owin); } } 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/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/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/SessionIdValidator.cs b/src/Umbraco.Web/Security/SessionIdValidator.cs index 9edae8da10..090b6c6dac 100644 --- a/src/Umbraco.Web/Security/SessionIdValidator.cs +++ b/src/Umbraco.Web/Security/SessionIdValidator.cs @@ -86,7 +86,7 @@ namespace Umbraco.Web.Security if (validate == false) return true; - var manager = owinCtx.Get(); + var manager = owinCtx.Get(); if (manager == null) return false; 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..6d3de9be1d 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 { @@ -71,8 +70,8 @@ namespace Umbraco.Web.Security } } - private BackOfficeUserManager _userManager; - protected BackOfficeUserManager UserManager + private BackOfficeOwinUserManager _userManager; + protected BackOfficeOwinUserManager UserManager => _userManager ?? (_userManager = _httpContextAccessor.GetRequiredHttpContext().GetOwinContext().GetBackOfficeUserManager()); [Obsolete("This needs to be removed, ASP.NET Identity should always be used for this operation, this is currently only used in the installer which needs to be updated")] diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index f8e73bb361..90d1d6829b 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -173,10 +173,6 @@ - - - - @@ -194,23 +190,17 @@ - - + - - - - - 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 42e0fe2940..48b3de44fd 100644 --- a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs @@ -2,10 +2,9 @@ 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.Persistence; using Umbraco.Core.Services; -using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; using Umbraco.Core.Mapping; using Umbraco.Web.Routing; @@ -30,7 +29,7 @@ namespace Umbraco.Web.WebApi [EnableDetailedErrors] public abstract class UmbracoAuthorizedApiController : UmbracoApiController { - private BackOfficeUserManager _userManager; + private BackOfficeOwinUserManager _userManager; protected UmbracoAuthorizedApiController() { @@ -44,7 +43,7 @@ namespace Umbraco.Web.WebApi /// /// Gets the user manager. /// - protected BackOfficeUserManager UserManager + protected BackOfficeOwinUserManager UserManager => _userManager ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager()); } }