Initial run

This commit is contained in:
Scott Brady
2020-04-02 08:08:09 +01:00
parent db87a9dd40
commit c8afd85bd3
10 changed files with 105 additions and 152 deletions

View File

@@ -211,7 +211,7 @@ namespace Umbraco.Web.Editors
if (passwordChangeResult.Success)
{
var userMgr = this.TryGetOwinContext().Result.GetBackOfficeUserManager();
var userMgr = this.TryGetOwinContext().Result.GetBackOfficeUserManager2();
//even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword
var result = new ModelWithNotifications<string>(passwordChangeResult.Result.ResetPassword);

View File

@@ -51,18 +51,7 @@ namespace Umbraco.Web
var ctx = owinContext.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
return ctx == null ? Attempt<HttpContextBase>.Fail() : Attempt.Succeed(ctx);
}
/// <summary>
/// Gets the back office sign in manager out of OWIN
/// </summary>
/// <param name="owinContext"></param>
/// <returns></returns>
public static BackOfficeSignInManager2 GetBackOfficeSignInManager(this IOwinContext owinContext)
{
return owinContext.Get<BackOfficeSignInManager2>()
?? throw new NullReferenceException($"Could not resolve an instance of {typeof (BackOfficeSignInManager2)} from the {typeof(IOwinContext)}.");
}
/// <summary>
/// Gets the back office sign in manager out of OWIN
/// </summary>
@@ -74,24 +63,6 @@ namespace Umbraco.Web
?? throw new NullReferenceException($"Could not resolve an instance of {typeof (BackOfficeSignInManager2)} from the {typeof(IOwinContext)}.");
}
/// <summary>
/// Gets the back office user manager out of OWIN
/// </summary>
/// <param name="owinContext"></param>
/// <returns></returns>
/// <remarks>
/// 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
/// </remarks>
public static BackOfficeUserManager2<BackOfficeIdentityUser> GetBackOfficeUserManager(this IOwinContext owinContext)
{
var marker = owinContext.Get<IBackOfficeUserManagerMarker>(BackOfficeUserManager2.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 (BackOfficeUserManager2<BackOfficeIdentityUser>)} from the {typeof (IOwinContext)}.");
}
/// <summary>
/// Gets the back office user manager out of OWIN
/// </summary>
@@ -110,8 +81,6 @@ namespace Umbraco.Web
?? throw new NullReferenceException($"Could not resolve an instance of {typeof (BackOfficeUserManager2<BackOfficeIdentityUser>)} from the {typeof (IOwinContext)}.");
}
// TODO: SB: OWIN DI
/// <summary>
/// Adapted from Microsoft.AspNet.Identity.Owin.OwinContextExtensions
/// </summary>

View File

@@ -2,7 +2,6 @@
using System.Threading;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Microsoft.Owin.Extensions;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
@@ -16,7 +15,6 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Mapping;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Net;
using Umbraco.Web.Composing;
@@ -53,11 +51,6 @@ namespace Umbraco.Web.Security
mapper,
passwordConfiguration,
ipResolver,
new OptionsWrapper<IdentityOptions>(new IdentityOptions()),
new UserAwarePasswordHasher2<BackOfficeIdentityUser>(new PasswordSecurity(passwordConfiguration)),
new[] {new UserValidator<BackOfficeIdentityUser>(),},
new[] {new PasswordValidator<BackOfficeIdentityUser>()},
new UpperInvariantLookupNormalizer(),
new IdentityErrorDescriber(),
null,
new NullLogger<BackOfficeUserManager2<BackOfficeIdentityUser>>()));
@@ -88,11 +81,6 @@ namespace Umbraco.Web.Security
passwordConfiguration,
ipResolver,
customUserStore,
new OptionsWrapper<IdentityOptions>(new IdentityOptions()),
new UserAwarePasswordHasher2<BackOfficeIdentityUser>(new PasswordSecurity(passwordConfiguration)),
new[] { new Microsoft.AspNetCore.Identity.UserValidator<BackOfficeIdentityUser>(), },
new[] { new PasswordValidator<BackOfficeIdentityUser>() },
new UpperInvariantLookupNormalizer(),
new IdentityErrorDescriber(),
null,
new NullLogger<BackOfficeUserManager2<BackOfficeIdentityUser>>()));

View File

@@ -1,51 +0,0 @@
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Umbraco.Core;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Security;
using Umbraco.Web.Models.Identity;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Security
{
public class BackOfficeClaimsIdentityFactory<T> : ClaimsIdentityFactory<T, int>
where T: BackOfficeIdentityUser
{
public BackOfficeClaimsIdentityFactory()
{
SecurityStampClaimType = Constants.Security.SessionIdClaimType;
UserNameClaimType = ClaimTypes.Name;
}
/// <summary>
/// Create a ClaimsIdentity from a user
/// </summary>
/// <param name="manager"/><param name="user"/><param name="authenticationType"/>
/// <returns/>
public override async Task<ClaimsIdentity> CreateAsync(UserManager<T, int> manager, T user, string authenticationType)
{
var baseIdentity = await base.CreateAsync(manager, user, authenticationType);
var umbracoIdentity = new UmbracoBackOfficeIdentity(baseIdentity,
user.Id,
user.UserName,
user.Name,
user.CalculatedContentStartNodeIds,
user.CalculatedMediaStartNodeIds,
user.Culture,
//NOTE - there is no session id assigned here, this is just creating the identity, a session id will be generated when the cookie is written
Guid.NewGuid().ToString(),
user.SecurityStamp,
user.AllowedSections,
user.Roles.Select(x => x.RoleId).ToArray());
return umbracoIdentity;
}
}
public class BackOfficeClaimsIdentityFactory : BackOfficeClaimsIdentityFactory<BackOfficeIdentityUser>
{ }
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Linq;
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;
namespace Umbraco.Web.Security
{
public class BackOfficeClaimsPrincipalFactory<TUser> : UserClaimsPrincipalFactory<TUser>
where TUser : BackOfficeIdentityUser
{
public BackOfficeClaimsPrincipalFactory(UserManager<TUser> userManager, IOptions<IdentityOptions> optionsAccessor)
: base(userManager, optionsAccessor)
{
}
public override async Task<ClaimsPrincipal> CreateAsync(TUser user)
{
var claimsPrincipal = await base.CreateAsync(user);
var umbracoIdentity = new UmbracoBackOfficeIdentity(
claimsPrincipal.Identity as ClaimsIdentity,
user.Id,
user.UserName,
user.Name,
user.CalculatedContentStartNodeIds,
user.CalculatedMediaStartNodeIds,
user.Culture,
//NOTE - there is no session id assigned here, this is just creating the identity, a session id will be generated when the cookie is written
Guid.NewGuid().ToString(),
user.SecurityStamp,
user.AllowedSections,
user.Roles.Select(x => x.RoleId).ToArray());
return new ClaimsPrincipal(umbracoIdentity);
}
}
}

View File

@@ -4,6 +4,7 @@ using System.Globalization;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Microsoft.Owin;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
@@ -16,11 +17,13 @@ namespace Umbraco.Web.Security
{
/// <summary>
/// Custom sign in manager due to SignInManager not being .NET Standard.
/// Code ported from Umbraco's BackOfficeSignInManager.
/// Can be removed once the web project moves to .NET Core.
/// </summary>
public class BackOfficeSignInManager2 : IDisposable
{
private readonly BackOfficeUserManager2<BackOfficeIdentityUser> _userManager;
private readonly IUserClaimsPrincipalFactory<BackOfficeIdentityUser> _claimsPrincipalFactory;
private readonly IAuthenticationManager _authenticationManager;
private readonly ILogger _logger;
private readonly IGlobalSettings _globalSettings;
@@ -28,27 +31,35 @@ namespace Umbraco.Web.Security
public BackOfficeSignInManager2(
BackOfficeUserManager2<BackOfficeIdentityUser> userManager,
IUserClaimsPrincipalFactory<BackOfficeIdentityUser> claimsPrincipalFactory,
IAuthenticationManager authenticationManager,
ILogger logger,
IGlobalSettings globalSettings,
IOwinRequest request)
{
_userManager = userManager ?? throw new ArgumentNullException(nameof(userManager));
_claimsPrincipalFactory = claimsPrincipalFactory ?? throw new ArgumentNullException(nameof(claimsPrincipalFactory));
_authenticationManager = authenticationManager ?? throw new ArgumentNullException(nameof(authenticationManager));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings));
_request = request ?? throw new ArgumentNullException(nameof(request));
}
public Task<ClaimsIdentity> CreateUserIdentityAsync(BackOfficeIdentityUser user)
public async Task<ClaimsIdentity> CreateUserIdentityAsync(BackOfficeIdentityUser user)
{
throw new NotImplementedException();
if (user == null) throw new ArgumentNullException(nameof(user));
var claimsPrincipal = await _claimsPrincipalFactory.CreateAsync(user);
return claimsPrincipal.Identity as ClaimsIdentity;
}
public static BackOfficeSignInManager2 Create(IOwinContext context, IGlobalSettings globalSettings, ILogger logger)
{
var userManager = context.GetBackOfficeUserManager2();
return new BackOfficeSignInManager2(
context.GetBackOfficeUserManager2(),
userManager,
new BackOfficeClaimsPrincipalFactory<BackOfficeIdentityUser>(userManager, new OptionsWrapper<IdentityOptions>(userManager.Options)),
context.Authentication,
logger,
globalSettings,
@@ -146,7 +157,7 @@ namespace Umbraco.Web.Security
if (requestContext != null)
{
var backofficeUserManager = requestContext.GetBackOfficeUserManager();
var backofficeUserManager = requestContext.GetBackOfficeUserManager2();
if (backofficeUserManager != null) backofficeUserManager.RaiseAccountLockedEvent(user.Id);
}
@@ -156,7 +167,7 @@ namespace Umbraco.Web.Security
if (requestContext != null)
{
var backofficeUserManager = requestContext.GetBackOfficeUserManager();
var backofficeUserManager = requestContext.GetBackOfficeUserManager2();
if (backofficeUserManager != null)
backofficeUserManager.RaiseInvalidLoginAttemptEvent(userName);
}

View File

@@ -23,22 +23,19 @@ namespace Umbraco.Web.Security
IIpResolver ipResolver,
IUserStore<BackOfficeIdentityUser> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<BackOfficeIdentityUser> passwordHasher,
IEnumerable<IUserValidator<BackOfficeIdentityUser>> userValidators,
IEnumerable<IPasswordValidator<BackOfficeIdentityUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<BackOfficeIdentityUser>> logger)
: base(passwordConfiguration, ipResolver, store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
: base(passwordConfiguration, ipResolver, store, optionsAccessor, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
InitUserManager(this, passwordConfiguration);
}
#region Static Create methods
// TODO: SB: Static Create methods for OWIN
/// <summary>
/// Creates a BackOfficeUserManager instance with all default options and the default BackOfficeUserManager
/// </summary>
@@ -50,25 +47,16 @@ namespace Umbraco.Web.Security
UmbracoMapper mapper,
IPasswordConfiguration passwordConfiguration,
IIpResolver ipResolver,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<BackOfficeIdentityUser> passwordHasher,
IEnumerable<IUserValidator<BackOfficeIdentityUser>> userValidators,
IEnumerable<IPasswordValidator<BackOfficeIdentityUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<BackOfficeIdentityUser>> logger)
{
var store = new BackOfficeUserStore2(userService, entityService, externalLoginService, globalSettings, mapper);
return new BackOfficeUserManager2(
return Create(
passwordConfiguration,
ipResolver,
store,
optionsAccessor,
passwordHasher,
userValidators,
passwordValidators,
keyNormalizer,
errors,
services,
logger);
@@ -81,24 +69,39 @@ namespace Umbraco.Web.Security
IPasswordConfiguration passwordConfiguration,
IIpResolver ipResolver,
IUserStore<BackOfficeIdentityUser> customUserStore,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<BackOfficeIdentityUser> passwordHasher,
IEnumerable<IUserValidator<BackOfficeIdentityUser>> userValidators,
IEnumerable<IPasswordValidator<BackOfficeIdentityUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<BackOfficeIdentityUser>> logger)
{
var options = new IdentityOptions();
// Configure validation logic for usernames
var userValidators = new List<UserValidator<BackOfficeIdentityUser>> { new BackOfficeUserValidator2<BackOfficeIdentityUser>() };
options.User.RequireUniqueEmail = true;
// Configure validation logic for passwords
var passwordValidators = new List<IPasswordValidator<BackOfficeIdentityUser>> { new PasswordValidator<BackOfficeIdentityUser>() };
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.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 BackOfficeUserManager2(
passwordConfiguration,
ipResolver,
customUserStore,
optionsAccessor,
passwordHasher,
new OptionsWrapper<IdentityOptions>(options),
userValidators,
passwordValidators,
keyNormalizer,
new NopLookupNormalizer(),
errors,
services,
logger);
@@ -117,14 +120,13 @@ namespace Umbraco.Web.Security
IIpResolver ipResolver,
IUserStore<T> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<T> passwordHasher,
IEnumerable<IUserValidator<T>> userValidators,
IEnumerable<IPasswordValidator<T>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<T>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
: base(store, optionsAccessor, null, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
PasswordConfiguration = passwordConfiguration ?? throw new ArgumentNullException(nameof(passwordConfiguration));
IpResolver = ipResolver ?? throw new ArgumentNullException(nameof(ipResolver));
@@ -146,7 +148,6 @@ namespace Umbraco.Web.Security
public override bool SupportsUserPhoneNumber => false;
#endregion
// TODO: SB: INIT
/// <summary>
/// Initializes the user manager with the correct options
/// </summary>
@@ -158,22 +159,8 @@ namespace Umbraco.Web.Security
IPasswordConfiguration passwordConfig)
// IDataProtectionProvider dataProtectionProvider
{
// Configure validation logic for usernames
manager.UserValidators.Clear();
manager.UserValidators.Add(new BackOfficeUserValidator2<T>());
manager.Options.User.RequireUniqueEmail = true;
// Configure validation logic for passwords
manager.PasswordValidators.Clear();
manager.PasswordValidators.Add(new PasswordValidator<T>());
manager.Options.Password.RequiredLength = passwordConfig.RequiredLength;
manager.Options.Password.RequireNonAlphanumeric = passwordConfig.RequireNonLetterOrDigit;
manager.Options.Password.RequireDigit = passwordConfig.RequireDigit;
manager.Options.Password.RequireLowercase = passwordConfig.RequireLowercase;
manager.Options.Password.RequireUppercase = passwordConfig.RequireUppercase;
//use a custom hasher based on our membership provider
manager.PasswordHasher = GetDefaultPasswordHasher(passwordConfig);
PasswordHasher = GetDefaultPasswordHasher(PasswordConfiguration);
// TODO: SB: manager.Options.Tokens using OWIN data protector
/*if (dataProtectionProvider != null)
@@ -184,12 +171,7 @@ namespace Umbraco.Web.Security
};
}*/
manager.Options.Lockout.AllowedForNewUsers = true;
manager.Options.Lockout.MaxFailedAccessAttempts = passwordConfig.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.
manager.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromDays(30);
}
/// <summary>
@@ -217,7 +199,6 @@ namespace Umbraco.Web.Security
return new UserAwarePasswordHasher2<T>(new PasswordSecurity(passwordConfiguration));
}
/// <summary>
/// Gets/sets the default back office user password checker
/// </summary>

View File

@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Identity;
namespace Umbraco.Web.Security
{
/// <summary>
/// No-op lookup normalizer to maintain compatibility with ASP.NET Identity 2
/// </summary>
public class NopLookupNormalizer : ILookupNormalizer
{
public string NormalizeName(string name) => name;
public string NormalizeEmail(string email) => email;
}
}

View File

@@ -198,6 +198,7 @@
<Compile Include="Runtime\AspNetUmbracoBootPermissionChecker.cs" />
<Compile Include="Scheduling\SchedulerComponent.cs" />
<Compile Include="Scheduling\SchedulerComposer.cs" />
<Compile Include="Security\BackOfficeClaimsPrincipalFactory.cs" />
<Compile Include="Security\BackOfficeSignInManager2.cs" />
<Compile Include="Security\BackOfficeUserManager2.cs" />
<Compile Include="Security\BackOfficeUserManagerMarker.cs" />
@@ -208,6 +209,7 @@
<Compile Include="Security\IUserSessionStore.cs" />
<Compile Include="Security\MembershipProviderBase.cs" />
<Compile Include="Security\MembershipProviderExtensions.cs" />
<Compile Include="Security\NopLookupNormalizer.cs" />
<Compile Include="Security\PasswordSecurity.cs" />
<Compile Include="Security\UmbracoBackOfficeIdentity.cs" />
<Compile Include="Security\UmbracoEmailMessage.cs" />
@@ -226,7 +228,6 @@
<Compile Include="Routing\RedirectTrackingComposer.cs" />
<Compile Include="Runtime\WebInitialComposer.cs" />
<Compile Include="Security\ActiveDirectoryBackOfficeUserPasswordChecker.cs" />
<Compile Include="Security\BackOfficeClaimsIdentityFactory.cs" />
<Compile Include="Security\BackOfficeUserPasswordCheckerResult.cs" />
<Compile Include="Security\IBackOfficeUserManagerMarker.cs" />
<Compile Include="Security\IBackOfficeUserPasswordChecker.cs" />

View File

@@ -113,7 +113,7 @@ namespace Umbraco.Web.WebApi.Filters
var owinCtx = actionContext.Request.TryGetOwinContext();
if (owinCtx)
{
var signInManager = owinCtx.Result.GetBackOfficeSignInManager();
var signInManager = owinCtx.Result.GetBackOfficeSignInManager2();
var backOfficeIdentityUser = Mapper.Map<BackOfficeIdentityUser>(user);
await signInManager.SignInAsync(backOfficeIdentityUser, isPersistent: true, rememberBrowser: false);