diff --git a/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserManager.cs b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserManager.cs
index 6d2ad081c3..99d0265d06 100644
--- a/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserManager.cs
+++ b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -56,7 +56,7 @@ namespace Umbraco.Core.BackOffice
// TODO: Support this
public override bool SupportsUserPhoneNumber => false;
#endregion
-
+
///
/// Used to validate a user's session
///
@@ -86,7 +86,7 @@ namespace Umbraco.Core.BackOffice
/// 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; }
///
@@ -332,57 +332,57 @@ namespace Umbraco.Core.BackOffice
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.Tests/Security/BackOfficeUserManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeUserManagerTests.cs
index 99a2b323dd..cf3d54a4ce 100644
--- a/src/Umbraco.Tests/Security/BackOfficeUserManagerTests.cs
+++ b/src/Umbraco.Tests/Security/BackOfficeUserManagerTests.cs
@@ -10,7 +10,7 @@ using Umbraco.Core.BackOffice;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Membership;
using Umbraco.Net;
-using BackOfficeUserManager = Umbraco.Web.Security.BackOfficeUserManager;
+using Umbraco.Web.Security;
namespace Umbraco.Tests.Security
{
@@ -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/UmbracoSecurityStampValidatorTests.cs b/src/Umbraco.Tests/Security/UmbracoSecurityStampValidatorTests.cs
index 4adfe15ad7..b80e526037 100644
--- a/src/Umbraco.Tests/Security/UmbracoSecurityStampValidatorTests.cs
+++ b/src/Umbraco.Tests/Security/UmbracoSecurityStampValidatorTests.cs
@@ -23,7 +23,7 @@ namespace Umbraco.Tests.Security
public class UmbracoSecurityStampValidatorTests
{
private Mock _mockOwinContext;
- private Mock> _mockUserManager;
+ private Mock _mockUserManager;
private Mock _mockSignInManager;
private AuthenticationTicket _testAuthTicket;
@@ -35,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));
}
@@ -43,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;
@@ -62,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((Umbraco.Web.Security.BackOfficeUserManager) null);
+ _mockOwinContext.Setup(x => x.Get(It.IsAny()))
+ .Returns((BackOfficeOwinUserManager) null);
var context = new CookieValidateIdentityContext(
_mockOwinContext.Object,
@@ -80,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()))
@@ -100,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))
@@ -123,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);
@@ -146,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);
@@ -170,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);
@@ -194,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;
@@ -202,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);
@@ -250,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,
@@ -267,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/Web/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs
index 80deb5a800..6905c749e6 100644
--- a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs
+++ b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs
@@ -469,7 +469,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>()))
@@ -509,9 +509,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/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/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs
index b38bbbb19d..d65bb043c5 100644
--- a/src/Umbraco.Web/Editors/AuthenticationController.cs
+++ b/src/Umbraco.Web/Editors/AuthenticationController.cs
@@ -40,7 +40,7 @@ namespace Umbraco.Web.Editors
[IsBackOffice]
public class AuthenticationController : UmbracoApiController
{
- private Security.BackOfficeUserManager _userManager;
+ private BackOfficeOwinUserManager _userManager;
private BackOfficeSignInManager _signInManager;
private readonly IUserPasswordConfiguration _passwordConfiguration;
private readonly IHostingEnvironment _hostingEnvironment;
@@ -71,8 +71,8 @@ namespace Umbraco.Web.Editors
_requestAccessor = requestAccessor ?? throw new ArgumentNullException(nameof(securitySettings));
}
- protected Security.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 782dea61ac..db7505aa3c 100644
--- a/src/Umbraco.Web/Editors/BackOfficeController.cs
+++ b/src/Umbraco.Web/Editors/BackOfficeController.cs
@@ -45,7 +45,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;
@@ -92,7 +92,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/PasswordChanger.cs b/src/Umbraco.Web/Editors/PasswordChanger.cs
index 10a5248045..256da61478 100644
--- a/src/Umbraco.Web/Editors/PasswordChanger.cs
+++ b/src/Umbraco.Web/Editors/PasswordChanger.cs
@@ -2,7 +2,6 @@
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Umbraco.Core;
-using Umbraco.Core.BackOffice;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Web.Models;
@@ -32,7 +31,7 @@ namespace Umbraco.Web.Editors
IUser currentUser,
IUser savingUser,
ChangingPasswordModel passwordModel,
- Security.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/OwinExtensions.cs b/src/Umbraco.Web/OwinExtensions.cs
index 67e9375ab5..4ea4040ec6 100644
--- a/src/Umbraco.Web/OwinExtensions.cs
+++ b/src/Umbraco.Web/OwinExtensions.cs
@@ -4,7 +4,6 @@ using Microsoft.Owin;
using Microsoft.Owin.Security;
using Umbraco.Core;
using Umbraco.Web.Security;
-using BackOfficeIdentityUser = Umbraco.Core.BackOffice.BackOfficeIdentityUser;
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/AppBuilderExtensions.cs b/src/Umbraco.Web/Security/AppBuilderExtensions.cs
index 6254dabeb9..81c5408e98 100644
--- a/src/Umbraco.Web/Security/AppBuilderExtensions.cs
+++ b/src/Umbraco.Web/Security/AppBuilderExtensions.cs
@@ -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/BackOfficeOwinUserManager.cs b/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs
new file mode 100644
index 0000000000..3a5ba8f706
--- /dev/null
+++ b/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs
@@ -0,0 +1,141 @@
+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.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 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/BackOfficeUserManager.cs b/src/Umbraco.Web/Security/BackOfficeUserManager.cs
deleted file mode 100644
index ed2df536e7..0000000000
--- a/src/Umbraco.Web/Security/BackOfficeUserManager.cs
+++ /dev/null
@@ -1,582 +0,0 @@
-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.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 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)
- {
- 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,
- IEnumerable> userValidators,
- IEnumerable> passwordValidators,
- ILookupNormalizer keyNormalizer,
- IdentityErrorDescriber errors,
- IServiceProvider services,
- ILogger> 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));
- }
-
- #region What we do not currently support
- // TODO: We could support this - but a user claims will mostly just be what is in the auth cookie
- public override bool SupportsUserClaim => false;
-
- // TODO: Support this
- public override bool SupportsQueryableUsers => false;
-
- ///
- /// Developers will need to override this to support custom 2 factor auth
- ///
- public override bool SupportsUserTwoFactor => false;
-
- // TODO: Support this
- 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
- ///
- ///
- ///
- ///
- public virtual async Task ValidateSessionIdAsync(string userId, string sessionId)
- {
- var userSessionStore = Store as IUserSessionStore;
- //if this is not set, for backwards compat (which would be super rare), we'll just approve it
- if (userSessionStore == null) return true;
-
- return await userSessionStore.ValidateSessionIdAsync(userId, sessionId);
- }
-
- ///
- /// This will determine which password hasher to use based on what is defined in config
- ///
- ///
- 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));
- }
-
- ///
- /// Gets/sets the default back office user password checker
- ///
- public IBackOfficeUserPasswordChecker BackOfficeUserPasswordChecker { get; set; }
- public IPasswordConfiguration PasswordConfiguration { get; }
- public IIpResolver IpResolver { get; }
-
- ///
- /// Helper method to generate a password for a user based on the current password validator
- ///
- ///
- public string GeneratePassword()
- {
- if (_passwordGenerator == null) _passwordGenerator = new PasswordGenerator(PasswordConfiguration);
- var password = _passwordGenerator.GeneratePassword();
- return password;
- }
-
- ///
- /// Override to check the user approval value as well as the user lock out date, by default this only checks the user's locked out date
- ///
- ///
- ///
- ///
- /// In the ASP.NET Identity world, there is only one value for being locked out, in Umbraco we have 2 so when checking this for Umbraco we need to check both values
- ///
- public override async Task IsLockedOutAsync(T user)
- {
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- if (user.IsApproved == false) return true;
-
- return await base.IsLockedOutAsync(user);
- }
-
- #region Overrides for password logic
-
- ///
- /// Logic used to validate a username and password
- ///
- ///
- ///
- ///
- ///
- /// By default this uses the standard ASP.Net Identity approach which is:
- /// * Get password store
- /// * Call VerifyPasswordAsync with the password store + user + password
- /// * Uses the PasswordHasher.VerifyHashedPassword to compare the stored password
- ///
- /// In some cases people want simple custom control over the username/password check, for simplicity
- /// sake, developers would like the users to simply validate against an LDAP directory but the user
- /// data remains stored inside of Umbraco.
- /// See: http://issues.umbraco.org/issue/U4-7032 for the use cases.
- ///
- /// We've allowed this check to be overridden with a simple callback so that developers don't actually
- /// have to implement/override this class.
- ///
- public override async Task CheckPasswordAsync(T user, string password)
- {
- if (BackOfficeUserPasswordChecker != null)
- {
- var result = await BackOfficeUserPasswordChecker.CheckPasswordAsync(user, password);
-
- if (user.HasIdentity == false)
- {
- return false;
- }
-
- //if the result indicates to not fallback to the default, then return true if the credentials are valid
- if (result != BackOfficeUserPasswordCheckerResult.FallbackToDefaultChecker)
- {
- return result == BackOfficeUserPasswordCheckerResult.ValidCredentials;
- }
- }
-
- //we cannot proceed if the user passed in does not have an identity
- if (user.HasIdentity == false)
- return false;
-
- //use the default behavior
- return await base.CheckPasswordAsync(user, password);
- }
-
- ///
- /// This is a special method that will reset the password but will raise the Password Changed event instead of the reset event
- ///
- ///
- ///
- ///
- ///
- ///
- /// We use this because in the back office the only way an admin can change another user's password without first knowing their password
- /// is to generate a token and reset it, however, when we do this we want to track a password change, not a password reset
- ///
- public async Task ChangePasswordWithResetAsync(int userId, string token, string newPassword)
- {
- var user = await base.FindByIdAsync(userId.ToString());
- if (user == null) throw new InvalidOperationException("Could not find user");
-
- var result = await base.ResetPasswordAsync(user, token, newPassword);
- if (result.Succeeded) RaisePasswordChangedEvent(userId);
- return result;
- }
-
- public override async Task ChangePasswordAsync(T user, string currentPassword, string newPassword)
- {
- var result = await base.ChangePasswordAsync(user, currentPassword, newPassword);
- if (result.Succeeded) RaisePasswordChangedEvent(user.Id);
- return result;
- }
-
- ///
- /// Override to determine how to hash the password
- ///
- ///
- ///
- ///
- ///
- ///
- /// This method is called anytime the password needs to be hashed for storage (i.e. including when reset password is used)
- ///
- protected override async Task UpdatePasswordHash(T user, string newPassword, bool validatePassword)
- {
- user.LastPasswordChangeDateUtc = DateTime.UtcNow;
-
- if (validatePassword)
- {
- var validate = await ValidatePasswordAsync(user, newPassword);
- if (!validate.Succeeded)
- {
- return validate;
- }
- }
-
- var passwordStore = Store as IUserPasswordStore;
- if (passwordStore == null) throw new NotSupportedException("The current user store does not implement " + typeof(IUserPasswordStore<>));
-
- var hash = newPassword != null ? PasswordHasher.HashPassword(user, newPassword) : null;
- await passwordStore.SetPasswordHashAsync(user, hash, CancellationToken);
- await UpdateSecurityStampInternal(user);
- return IdentityResult.Success;
- }
-
- ///
- /// This is copied from the underlying .NET base class since they decided to not expose it
- ///
- ///
- ///
- private async Task UpdateSecurityStampInternal(T user)
- {
- if (SupportsUserSecurityStamp == false) return;
- await GetSecurityStore().SetSecurityStampAsync(user, NewSecurityStamp(), CancellationToken.None);
- }
-
- ///
- /// This is copied from the underlying .NET base class since they decided to not expose it
- ///
- ///
- private IUserSecurityStampStore GetSecurityStore()
- {
- var store = Store as IUserSecurityStampStore;
- if (store == null) throw new NotSupportedException("The current user store does not implement " + typeof(IUserSecurityStampStore<>));
- return store;
- }
-
- ///
- /// This is copied from the underlying .NET base class since they decided to not expose it
- ///
- ///
- private static string NewSecurityStamp()
- {
- return Guid.NewGuid().ToString();
- }
-
- #endregion
-
- public override async Task SetLockoutEndDateAsync(T user, DateTimeOffset? lockoutEnd)
- {
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- var result = await base.SetLockoutEndDateAsync(user, lockoutEnd);
-
- // The way we unlock is by setting the lockoutEnd date to the current datetime
- if (result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow)
- {
- RaiseAccountLockedEvent(user.Id);
- }
- else
- {
- RaiseAccountUnlockedEvent(user.Id);
- //Resets the login attempt fails back to 0 when unlock is clicked
- await ResetAccessFailedCountAsync(user);
- }
-
- return result;
- }
-
- public override async Task ResetAccessFailedCountAsync(T user)
- {
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- var lockoutStore = (IUserLockoutStore)Store;
- var accessFailedCount = await GetAccessFailedCountAsync(user);
-
- if (accessFailedCount == 0)
- return IdentityResult.Success;
-
- await lockoutStore.ResetAccessFailedCountAsync(user, CancellationToken.None);
- //raise the event now that it's reset
- RaiseResetAccessFailedCountEvent(user.Id);
- return await UpdateAsync(user);
- }
-
- ///
- /// Overrides the Microsoft ASP.NET user management method
- ///
- ///
- ///
- /// returns a Async Task
- ///
- ///
- /// Doesn't set fail attempts back to 0
- ///
- public override async Task AccessFailedAsync(T user)
- {
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- var lockoutStore = Store as IUserLockoutStore;
- if (lockoutStore == null) throw new NotSupportedException("The current user store does not implement " + typeof(IUserLockoutStore<>));
-
- var count = await lockoutStore.IncrementAccessFailedCountAsync(user, CancellationToken.None);
-
- if (count >= Options.Lockout.MaxFailedAccessAttempts)
- {
- await lockoutStore.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan),
- CancellationToken.None);
- //NOTE: in normal aspnet identity this would do set the number of failed attempts back to 0
- //here we are persisting the value for the back office
- }
-
- var result = await UpdateAsync(user);
-
- //Slightly confusing: this will return a Success if we successfully update the AccessFailed count
- if (result.Succeeded) RaiseLoginFailedEvent(user.Id);
-
- return result;
- }
-
- internal void RaiseAccountLockedEvent(int userId)
- {
- OnAccountLocked(new IdentityAuditEventArgs(AuditEvent.AccountLocked, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
- }
-
- internal void RaiseAccountUnlockedEvent(int userId)
- {
- OnAccountUnlocked(new IdentityAuditEventArgs(AuditEvent.AccountUnlocked, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
- }
-
- internal void RaiseForgotPasswordRequestedEvent(int userId)
- {
- OnForgotPasswordRequested(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordRequested, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
- }
-
- internal void RaiseForgotPasswordChangedSuccessEvent(int userId)
- {
- OnForgotPasswordChangedSuccess(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordChangedSuccess, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
- }
-
- internal void RaiseLoginFailedEvent(int userId)
- {
- OnLoginFailed(new IdentityAuditEventArgs(AuditEvent.LoginFailed, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
- }
-
- internal 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)
- {
- OnLoginRequiresVerification(new IdentityAuditEventArgs(AuditEvent.LoginRequiresVerification, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
- }
-
- internal void RaiseLoginSuccessEvent(int userId)
- {
- OnLoginSuccess(new IdentityAuditEventArgs(AuditEvent.LoginSucces, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
- }
-
- internal void RaiseLogoutSuccessEvent(int userId)
- {
- OnLogoutSuccess(new IdentityAuditEventArgs(AuditEvent.LogoutSuccess, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
- }
-
- internal void RaisePasswordChangedEvent(int userId)
- {
- OnPasswordChanged(new IdentityAuditEventArgs(AuditEvent.PasswordChanged, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
- }
-
- internal void RaiseResetAccessFailedCountEvent(int userId)
- {
- OnResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.ResetAccessFailedCount, IpResolver.GetCurrentRequestIpAddress(), affectedUser: userId));
- }
-
- public static event EventHandler AccountLocked;
- public static event EventHandler AccountUnlocked;
- public static event EventHandler ForgotPasswordRequested;
- public static event EventHandler ForgotPasswordChangedSuccess;
- public static event EventHandler LoginFailed;
- public static event EventHandler LoginRequiresVerification;
- public static event EventHandler LoginSuccess;
- public static event EventHandler LogoutSuccess;
- public static event EventHandler PasswordChanged;
- public static event EventHandler PasswordReset;
- public static event EventHandler ResetAccessFailedCount;
-
- protected virtual void OnAccountLocked(IdentityAuditEventArgs e)
- {
- if (AccountLocked != null) AccountLocked(this, e);
- }
-
- protected virtual void OnAccountUnlocked(IdentityAuditEventArgs e)
- {
- if (AccountUnlocked != null) AccountUnlocked(this, e);
- }
-
- protected virtual void OnForgotPasswordRequested(IdentityAuditEventArgs e)
- {
- if (ForgotPasswordRequested != null) ForgotPasswordRequested(this, e);
- }
-
- protected virtual void OnForgotPasswordChangedSuccess(IdentityAuditEventArgs e)
- {
- if (ForgotPasswordChangedSuccess != null) ForgotPasswordChangedSuccess(this, e);
- }
-
- protected virtual void OnLoginFailed(IdentityAuditEventArgs e)
- {
- if (LoginFailed != null) LoginFailed(this, e);
- }
-
- protected virtual void OnLoginRequiresVerification(IdentityAuditEventArgs e)
- {
- if (LoginRequiresVerification != null) LoginRequiresVerification(this, e);
- }
-
- protected virtual void OnLoginSuccess(IdentityAuditEventArgs e)
- {
- if (LoginSuccess != null) LoginSuccess(this, e);
- }
-
- protected virtual void OnLogoutSuccess(IdentityAuditEventArgs e)
- {
- if (LogoutSuccess != null) LogoutSuccess(this, e);
- }
-
- protected virtual void OnPasswordChanged(IdentityAuditEventArgs e)
- {
- if (PasswordChanged != null) PasswordChanged(this, e);
- }
-
- protected virtual void OnPasswordReset(IdentityAuditEventArgs e)
- {
- if (PasswordReset != null) PasswordReset(this, e);
- }
-
- protected virtual void OnResetAccessFailedCount(IdentityAuditEventArgs e)
- {
- if (ResetAccessFailedCount != null) ResetAccessFailedCount(this, e);
- }
- }
-}
diff --git a/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs b/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs
index d9d3f0e6f6..dd657b48bf 100644
--- a/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs
+++ b/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs
@@ -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/BackOfficeUserStore.cs b/src/Umbraco.Web/Security/BackOfficeUserStore.cs
deleted file mode 100644
index d9d1900951..0000000000
--- a/src/Umbraco.Web/Security/BackOfficeUserStore.cs
+++ /dev/null
@@ -1,917 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Identity;
-using Umbraco.Core;
-using Umbraco.Core.BackOffice;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Mapping;
-using Umbraco.Core.Models;
-using Umbraco.Core.Models.Identity;
-using Umbraco.Core.Models.Membership;
-using Umbraco.Core.Services;
-
-namespace Umbraco.Web.Security
-{
- public class BackOfficeUserStore : DisposableObjectSlim,
- IUserPasswordStore,
- IUserEmailStore,
- IUserLoginStore,
- IUserRoleStore,
- IUserSecurityStampStore,
- IUserLockoutStore,
- IUserTwoFactorStore,
- IUserSessionStore
-
- // TODO: This would require additional columns/tables for now people will need to implement this on their own
- //IUserPhoneNumberStore,
- // TODO: To do this we need to implement IQueryable - we'll have an IQuerable implementation soon with the UmbracoLinqPadDriver implementation
- //IQueryableUserStore
- {
- private readonly IUserService _userService;
- private readonly IEntityService _entityService;
- private readonly IExternalLoginService _externalLoginService;
- private readonly IGlobalSettings _globalSettings;
- private readonly UmbracoMapper _mapper;
- private bool _disposed = false;
-
- public BackOfficeUserStore(IUserService userService, IEntityService entityService, IExternalLoginService externalLoginService, IGlobalSettings globalSettings, UmbracoMapper mapper)
- {
- _userService = userService;
- _entityService = entityService;
- _externalLoginService = externalLoginService;
- _globalSettings = globalSettings;
- if (userService == null) throw new ArgumentNullException("userService");
- if (externalLoginService == null) throw new ArgumentNullException("externalLoginService");
- _mapper = mapper;
-
- _userService = userService;
- _externalLoginService = externalLoginService;
- }
-
- ///
- /// Handles the disposal of resources. Derived from abstract class which handles common required locking logic.
- ///
- protected override void DisposeResources()
- {
- _disposed = true;
- }
-
- public Task GetUserIdAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- return Task.FromResult(user.Id.ToString());
- }
-
- public Task GetUserNameAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- return Task.FromResult(user.UserName);
- }
-
- public Task SetUserNameAsync(BackOfficeIdentityUser user, string userName, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- user.UserName = userName;
- return Task.CompletedTask;
- }
-
- public Task GetNormalizedUserNameAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken)
- {
- return GetUserNameAsync(user, cancellationToken);
- }
-
- public Task SetNormalizedUserNameAsync(BackOfficeIdentityUser user, string normalizedName, CancellationToken cancellationToken)
- {
- return SetUserNameAsync(user, normalizedName, cancellationToken);
- }
-
- ///
- /// Insert a new user
- ///
- ///
- ///
- ///
- public Task CreateAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- //the password must be 'something' it could be empty if authenticating
- // with an external provider so we'll just generate one and prefix it, the
- // prefix will help us determine if the password hasn't actually been specified yet.
- //this will hash the guid with a salt so should be nicely random
- var aspHasher = new PasswordHasher();
- var emptyPasswordValue = Constants.Security.EmptyPasswordPrefix +
- aspHasher.HashPassword(user, Guid.NewGuid().ToString("N"));
-
- var userEntity = new User(_globalSettings, user.Name, user.Email, user.UserName, emptyPasswordValue)
- {
- Language = user.Culture ?? _globalSettings.DefaultUILanguage,
- StartContentIds = user.StartContentIds ?? new int[] { },
- StartMediaIds = user.StartMediaIds ?? new int[] { },
- IsLockedOut = user.IsLockedOut,
- };
-
- UpdateMemberProperties(userEntity, user);
-
- // TODO: We should deal with Roles --> User Groups here which we currently are not doing
-
- _userService.Save(userEntity);
-
- if (!userEntity.HasIdentity) throw new DataException("Could not create the user, check logs for details");
-
- //re-assign id
- user.Id = userEntity.Id;
-
- return Task.FromResult(IdentityResult.Success);
- }
-
- ///
- /// Update a user
- ///
- ///
- ///
- ///
- public async Task UpdateAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- var found = _userService.GetUserById(user.Id);
- if (found != null)
- {
- // we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it.
- var isLoginsPropertyDirty = user.IsPropertyDirty("Logins");
-
- if (UpdateMemberProperties(found, user))
- {
- _userService.Save(found);
- }
-
- if (isLoginsPropertyDirty)
- {
- var logins = await GetLoginsAsync(user);
- _externalLoginService.SaveUserLogins(found.Id, logins.Select(UserLoginInfoWrapper.Wrap));
- }
- }
-
- return IdentityResult.Success;
- }
-
- ///
- /// Delete a user
- ///
- ///
- ///
- public Task DeleteAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- var found = _userService.GetUserById(user.Id);
- if (found != null)
- {
- _userService.Delete(found);
- }
- _externalLoginService.DeleteUserLogins(user.Id);
-
- return Task.FromResult(IdentityResult.Success);
- }
-
- ///
- /// Finds a user
- ///
- ///
- ///
- ///
- public async Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
-
- var user = _userService.GetUserById(UserIdToInt(userId));
- if (user == null) return null;
-
- return await Task.FromResult(AssignLoginsCallback(_mapper.Map(user)));
- }
-
- ///
- /// Find a user by name
- ///
- ///
- ///
- ///
- public async Task FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- var user = _userService.GetByUsername(userName);
- if (user == null)
- {
- return null;
- }
-
- var result = AssignLoginsCallback(_mapper.Map(user));
-
- return await Task.FromResult(result);
- }
-
- ///
- /// Set the user password hash
- ///
- ///
- ///
- ///
- public Task SetPasswordHashAsync(BackOfficeIdentityUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
- if (passwordHash == null) throw new ArgumentNullException(nameof(passwordHash));
- if (string.IsNullOrEmpty(passwordHash)) throw new ArgumentException("Value can't be empty.", nameof(passwordHash));
-
- user.PasswordHash = passwordHash;
-
- return Task.CompletedTask;
- }
-
- ///
- /// Get the user password hash
- ///
- ///
- ///
- ///
- public Task GetPasswordHashAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- return Task.FromResult(user.PasswordHash);
- }
-
- ///
- /// Returns true if a user has a password set
- ///
- ///
- ///
- ///
- public Task HasPasswordAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- return Task.FromResult(string.IsNullOrEmpty(user.PasswordHash) == false);
- }
-
- ///
- /// Set the user email
- ///
- ///
- ///
- ///
- public Task SetEmailAsync(BackOfficeIdentityUser user, string email, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
- if (email.IsNullOrWhiteSpace()) throw new ArgumentNullException(nameof(email));
-
- user.Email = email;
-
- return Task.CompletedTask;
- }
-
- ///
- /// Get the user email
- ///
- ///
- ///
- ///
- public Task GetEmailAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- return Task.FromResult(user.Email);
- }
-
- ///
- /// Returns true if the user email is confirmed
- ///
- ///
- ///
- ///
- public Task GetEmailConfirmedAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- return Task.FromResult(user.EmailConfirmed);
- }
-
- ///
- /// Sets whether the user email is confirmed
- ///
- ///
- ///
- ///
- public Task SetEmailConfirmedAsync(BackOfficeIdentityUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- user.EmailConfirmed = confirmed;
- return Task.CompletedTask;
- }
-
- ///
- /// Returns the user associated with this email
- ///
- ///
- ///
- ///
- public Task FindByEmailAsync(string email, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- var user = _userService.GetByEmail(email);
- var result = user == null
- ? null
- : _mapper.Map(user);
-
- return Task.FromResult(AssignLoginsCallback(result));
- }
-
- public Task GetNormalizedEmailAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken)
- {
- return GetEmailAsync(user, cancellationToken);
- }
-
- public Task SetNormalizedEmailAsync(BackOfficeIdentityUser user, string normalizedEmail, CancellationToken cancellationToken)
- {
- return SetEmailAsync(user, normalizedEmail, cancellationToken);
- }
-
- ///
- /// Adds a user login with the specified provider and key
- ///
- ///
- ///
- ///
- ///
- public Task AddLoginAsync(BackOfficeIdentityUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
- if (login == null) throw new ArgumentNullException(nameof(login));
-
- var logins = user.Logins;
- var instance = new IdentityUserLogin(login.LoginProvider, login.ProviderKey, user.Id);
- var userLogin = instance;
- logins.Add(userLogin);
-
- return Task.CompletedTask;
- }
-
- ///
- /// Removes the user login with the specified combination if it exists
- ///
- ///
- ///
- ///
- ///
- ///
- public Task RemoveLoginAsync(BackOfficeIdentityUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- var userLogin = user.Logins.SingleOrDefault(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey);
- if (userLogin != null) user.Logins.Remove(userLogin);
-
- return Task.CompletedTask;
- }
-
- ///
- /// Returns the linked accounts for this user
- ///
- ///
- ///
- ///
- public Task> GetLoginsAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
- return Task.FromResult((IList)
- user.Logins.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.LoginProvider)).ToList());
- }
-
- ///
- /// Returns the user associated with this login
- ///
- ///
- public Task FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
-
- //get all logins associated with the login id
- var result = _externalLoginService.Find(UserLoginInfoWrapper.Wrap(new UserLoginInfo(loginProvider, providerKey, loginProvider))).ToArray();
- if (result.Any())
- {
- //return the first user that matches the result
- BackOfficeIdentityUser output = null;
- foreach (var l in result)
- {
- var user = _userService.GetUserById(l.UserId);
- if (user != null)
- {
- output = _mapper.Map(user);
- break;
- }
- }
-
- return Task.FromResult(AssignLoginsCallback(output));
- }
-
- return Task.FromResult(null);
- }
-
-
- ///
- /// Adds a user to a role (user group)
- ///
- ///
- ///
- ///
- ///
- public Task AddToRoleAsync(BackOfficeIdentityUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
- if (normalizedRoleName == null) throw new ArgumentNullException(nameof(normalizedRoleName));
- if (string.IsNullOrWhiteSpace(normalizedRoleName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(normalizedRoleName));
-
- var userRole = user.Roles.SingleOrDefault(r => r.RoleId == normalizedRoleName);
-
- if (userRole == null)
- {
- user.AddRole(normalizedRoleName);
- }
-
- return Task.CompletedTask;
- }
-
- ///
- /// Removes the role (user group) for the user
- ///
- ///
- ///
- ///
- ///
- public Task RemoveFromRoleAsync(BackOfficeIdentityUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (user == null) throw new ArgumentNullException(nameof(user));
- if (normalizedRoleName == null) throw new ArgumentNullException(nameof(normalizedRoleName));
- if (string.IsNullOrWhiteSpace(normalizedRoleName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(normalizedRoleName));
-
- var userRole = user.Roles.SingleOrDefault(r => r.RoleId == normalizedRoleName);
-
- if (userRole != null)
- {
- user.Roles.Remove(userRole);
- }
-
- return Task.CompletedTask;
- }
-
- ///
- /// Returns the roles (user groups) for this user
- ///
- ///
- ///
- ///
- public Task> GetRolesAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
- return Task.FromResult((IList)user.Roles.Select(x => x.RoleId).ToList());
- }
-
- ///
- /// Returns true if a user is in the role
- ///
- ///
- ///
- ///
- ///
- public Task IsInRoleAsync(BackOfficeIdentityUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
- return Task.FromResult(user.Roles.Select(x => x.RoleId).InvariantContains(normalizedRoleName));
- }
-
- ///
- /// Lists all users of a given role.
- ///
- ///
- /// Identity Role names are equal to Umbraco UserGroup alias.
- ///
- public Task> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (normalizedRoleName == null) throw new ArgumentNullException(nameof(normalizedRoleName));
-
- var userGroup = _userService.GetUserGroupByAlias(normalizedRoleName);
-
- var users = _userService.GetAllInGroup(userGroup.Id);
- IList backOfficeIdentityUsers = users.Select(x => _mapper.Map(x)).ToList();
-
- return Task.FromResult(backOfficeIdentityUsers);
- }
-
- ///
- /// Set the security stamp for the user
- ///
- ///
- ///
- ///
- ///
- public Task SetSecurityStampAsync(BackOfficeIdentityUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- user.SecurityStamp = stamp;
- return Task.CompletedTask;
- }
-
- ///
- /// Get the user security stamp
- ///
- ///
- ///
- ///
- public Task GetSecurityStampAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- //the stamp cannot be null, so if it is currently null then we'll just return a hash of the password
- return Task.FromResult(user.SecurityStamp.IsNullOrWhiteSpace()
- ? user.PasswordHash.GenerateHash()
- : user.SecurityStamp);
- }
-
- private BackOfficeIdentityUser AssignLoginsCallback(BackOfficeIdentityUser user)
- {
- if (user != null)
- {
- user.SetLoginsCallback(new Lazy>(() =>
- _externalLoginService.GetAll(user.Id)));
- }
- return user;
- }
-
- ///
- /// Sets whether two factor authentication is enabled for the user
- ///
- ///
- ///
- ///
- ///
- public virtual Task SetTwoFactorEnabledAsync(BackOfficeIdentityUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
-
- user.TwoFactorEnabled = false;
- return Task.CompletedTask;
- }
-
- ///
- /// Returns whether two factor authentication is enabled for the user
- ///
- ///
- ///
- public virtual Task GetTwoFactorEnabledAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
-
- return Task.FromResult(false);
- }
-
- #region IUserLockoutStore
-
- ///
- /// Returns the DateTimeOffset that represents the end of a user's lockout, any time in the past should be considered not locked out.
- ///
- ///
- ///
- ///
- ///
- /// Currently we do not support a timed lock out, when they are locked out, an admin will have to reset the status
- ///
- public Task GetLockoutEndDateAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- return user.LockoutEndDateUtc.HasValue
- ? Task.FromResult(DateTimeOffset.MaxValue)
- : Task.FromResult(DateTimeOffset.MinValue);
- }
-
- ///
- /// Locks a user out until the specified end date (set to a past date, to unlock a user)
- ///
- ///
- ///
- ///
- ///
- /// Currently we do not support a timed lock out, when they are locked out, an admin will have to reset the status
- ///
- public Task SetLockoutEndDateAsync(BackOfficeIdentityUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- user.LockoutEndDateUtc = lockoutEnd.Value.UtcDateTime;
- return Task.CompletedTask;
- }
-
- ///
- /// Used to record when an attempt to access the user has failed
- ///
- ///
- ///
- ///
- public Task IncrementAccessFailedCountAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- user.AccessFailedCount++;
- return Task.FromResult(user.AccessFailedCount);
- }
-
- ///
- /// Used to reset the access failed count, typically after the account is successfully accessed
- ///
- ///
- ///
- ///
- public Task ResetAccessFailedCountAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- user.AccessFailedCount = 0;
- return Task.CompletedTask;
- }
-
- ///
- /// Returns the current number of failed access attempts. This number usually will be reset whenever the password is
- /// verified or the account is locked out.
- ///
- ///
- ///
- ///
- public Task GetAccessFailedCountAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
- return Task.FromResult(user.AccessFailedCount);
- }
-
- ///
- /// Returns true
- ///
- ///
- ///
- ///
- public Task GetLockoutEnabledAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
- return Task.FromResult(user.LockoutEnabled);
- }
-
- ///
- /// Doesn't actually perform any function, users can always be locked out
- ///
- ///
- ///
- ///
- public Task SetLockoutEnabledAsync(BackOfficeIdentityUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken))
- {
- cancellationToken.ThrowIfCancellationRequested();
- ThrowIfDisposed();
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- user.LockoutEnabled = enabled;
- return Task.CompletedTask;
- }
- #endregion
-
- private bool UpdateMemberProperties(IUser user, BackOfficeIdentityUser identityUser)
- {
- var anythingChanged = false;
-
- //don't assign anything if nothing has changed as this will trigger the track changes of the model
-
- if (identityUser.IsPropertyDirty("LastLoginDateUtc")
- || (user.LastLoginDate != default(DateTime) && identityUser.LastLoginDateUtc.HasValue == false)
- || identityUser.LastLoginDateUtc.HasValue && user.LastLoginDate.ToUniversalTime() != identityUser.LastLoginDateUtc.Value)
- {
- anythingChanged = true;
- //if the LastLoginDate is being set to MinValue, don't convert it ToLocalTime
- var dt = identityUser.LastLoginDateUtc == DateTime.MinValue ? DateTime.MinValue : identityUser.LastLoginDateUtc.Value.ToLocalTime();
- user.LastLoginDate = dt;
- }
- if (identityUser.IsPropertyDirty("LastPasswordChangeDateUtc")
- || (user.LastPasswordChangeDate != default(DateTime) && identityUser.LastPasswordChangeDateUtc.HasValue == false)
- || identityUser.LastPasswordChangeDateUtc.HasValue && user.LastPasswordChangeDate.ToUniversalTime() != identityUser.LastPasswordChangeDateUtc.Value)
- {
- anythingChanged = true;
- user.LastPasswordChangeDate = identityUser.LastPasswordChangeDateUtc.Value.ToLocalTime();
- }
- if (identityUser.IsPropertyDirty("EmailConfirmed")
- || (user.EmailConfirmedDate.HasValue && user.EmailConfirmedDate.Value != default(DateTime) && identityUser.EmailConfirmed == false)
- || ((user.EmailConfirmedDate.HasValue == false || user.EmailConfirmedDate.Value == default(DateTime)) && identityUser.EmailConfirmed))
- {
- anythingChanged = true;
- user.EmailConfirmedDate = identityUser.EmailConfirmed ? (DateTime?)DateTime.Now : null;
- }
- if (identityUser.IsPropertyDirty("Name")
- && user.Name != identityUser.Name && identityUser.Name.IsNullOrWhiteSpace() == false)
- {
- anythingChanged = true;
- user.Name = identityUser.Name;
- }
- if (identityUser.IsPropertyDirty("Email")
- && user.Email != identityUser.Email && identityUser.Email.IsNullOrWhiteSpace() == false)
- {
- anythingChanged = true;
- user.Email = identityUser.Email;
- }
- if (identityUser.IsPropertyDirty("AccessFailedCount")
- && user.FailedPasswordAttempts != identityUser.AccessFailedCount)
- {
- anythingChanged = true;
- user.FailedPasswordAttempts = identityUser.AccessFailedCount;
- }
- if (user.IsLockedOut != identityUser.IsLockedOut)
- {
- anythingChanged = true;
- user.IsLockedOut = identityUser.IsLockedOut;
-
- if (user.IsLockedOut)
- {
- //need to set the last lockout date
- user.LastLockoutDate = DateTime.Now;
- }
-
- }
- if (identityUser.IsPropertyDirty("UserName")
- && user.Username != identityUser.UserName && identityUser.UserName.IsNullOrWhiteSpace() == false)
- {
- anythingChanged = true;
- user.Username = identityUser.UserName;
- }
- if (identityUser.IsPropertyDirty("PasswordHash")
- && user.RawPasswordValue != identityUser.PasswordHash && identityUser.PasswordHash.IsNullOrWhiteSpace() == false)
- {
- anythingChanged = true;
- user.RawPasswordValue = identityUser.PasswordHash;
- }
-
- if (identityUser.IsPropertyDirty("Culture")
- && user.Language != identityUser.Culture && identityUser.Culture.IsNullOrWhiteSpace() == false)
- {
- anythingChanged = true;
- user.Language = identityUser.Culture;
- }
- if (identityUser.IsPropertyDirty("StartMediaIds")
- && user.StartMediaIds.UnsortedSequenceEqual(identityUser.StartMediaIds) == false)
- {
- anythingChanged = true;
- user.StartMediaIds = identityUser.StartMediaIds;
- }
- if (identityUser.IsPropertyDirty("StartContentIds")
- && user.StartContentIds.UnsortedSequenceEqual(identityUser.StartContentIds) == false)
- {
- anythingChanged = true;
- user.StartContentIds = identityUser.StartContentIds;
- }
- if (user.SecurityStamp != identityUser.SecurityStamp)
- {
- anythingChanged = true;
- user.SecurityStamp = identityUser.SecurityStamp;
- }
-
- // TODO: Fix this for Groups too
- if (identityUser.IsPropertyDirty("Roles") || identityUser.IsPropertyDirty("Groups"))
- {
- var userGroupAliases = user.Groups.Select(x => x.Alias).ToArray();
-
- var identityUserRoles = identityUser.Roles.Select(x => x.RoleId).ToArray();
- var identityUserGroups = identityUser.Groups.Select(x => x.Alias).ToArray();
-
- var combinedAliases = identityUserRoles.Union(identityUserGroups).ToArray();
-
- if (userGroupAliases.ContainsAll(combinedAliases) == false
- || combinedAliases.ContainsAll(userGroupAliases) == false)
- {
- anythingChanged = true;
-
- //clear out the current groups (need to ToArray since we are modifying the iterator)
- user.ClearGroups();
-
- //go lookup all these groups
- var groups = _userService.GetUserGroupsByAlias(combinedAliases).Select(x => x.ToReadOnlyGroup()).ToArray();
-
- //use all of the ones assigned and add them
- foreach (var group in groups)
- {
- user.AddGroup(group);
- }
-
- //re-assign
- identityUser.Groups = groups;
- }
- }
-
- //we should re-set the calculated start nodes
- identityUser.CalculatedMediaStartNodeIds = user.CalculateMediaStartNodeIds(_entityService);
- identityUser.CalculatedContentStartNodeIds = user.CalculateContentStartNodeIds(_entityService);
-
- //reset all changes
- identityUser.ResetDirtyProperties(false);
-
- return anythingChanged;
- }
-
- private void ThrowIfDisposed()
- {
- if (_disposed) throw new ObjectDisposedException(GetType().Name);
- }
-
- public Task ValidateSessionIdAsync(string userId, string sessionId)
- {
- Guid guidSessionId;
- if (Guid.TryParse(sessionId, out guidSessionId))
- {
- return Task.FromResult(_userService.ValidateLoginSession(UserIdToInt(userId), guidSessionId));
- }
-
- return Task.FromResult(false);
- }
-
- private static int UserIdToInt(string userId)
- {
- var attempt = userId.TryConvertTo();
- if (attempt.Success) return attempt.Result;
-
- throw new InvalidOperationException("Unable to convert user ID to int", attempt.Exception);
- }
- }
-}
diff --git a/src/Umbraco.Web/Security/BackOfficeUserValidator.cs b/src/Umbraco.Web/Security/BackOfficeUserValidator.cs
deleted file mode 100644
index 951bdee4ed..0000000000
--- a/src/Umbraco.Web/Security/BackOfficeUserValidator.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Identity;
-using Umbraco.Core.BackOffice;
-
-namespace Umbraco.Web.Security
-{
- public class BackOfficeUserValidator : UserValidator
- where T : BackOfficeIdentityUser
- {
- public override async Task ValidateAsync(UserManager manager, T user)
- {
- // Don't validate if the user's email or username hasn't changed otherwise it's just wasting SQL queries.
- if (user.IsPropertyDirty("Email") || user.IsPropertyDirty("UserName"))
- {
- return await base.ValidateAsync(manager, user);
- }
- return IdentityResult.Success;
- }
- }
-}
diff --git a/src/Umbraco.Web/Security/IBackOfficeUserManagerMarker.cs b/src/Umbraco.Web/Security/IBackOfficeUserManagerMarker.cs
index 2b1f9927f2..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.Core.BackOffice;
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/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/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs
index 3c17bd370a..6d3de9be1d 100644
--- a/src/Umbraco.Web/Security/WebSecurity.cs
+++ b/src/Umbraco.Web/Security/WebSecurity.cs
@@ -70,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 9817431f76..3bc0b68906 100755
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -195,12 +195,10 @@
-
+
-
-
diff --git a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs
index b171356e23..f0c79b24f2 100644
--- a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs
+++ b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs
@@ -3,7 +3,6 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Web.WebApi.Filters;
-using Umbraco.Core.BackOffice;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Web.Security;
@@ -30,7 +29,7 @@ namespace Umbraco.Web.WebApi
[EnableDetailedErrors]
public abstract class UmbracoAuthorizedApiController : UmbracoApiController
{
- private Security.BackOfficeUserManager _userManager;
+ private BackOfficeOwinUserManager _userManager;
protected UmbracoAuthorizedApiController()
{
@@ -44,7 +43,7 @@ namespace Umbraco.Web.WebApi
///
/// Gets the user manager.
///
- protected Security.BackOfficeUserManager UserManager
+ protected BackOfficeOwinUserManager UserManager
=> _userManager ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager());
}
}