diff --git a/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs
index 7ac3701c5c..cdd81a3fe3 100644
--- a/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs
+++ b/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserStore.cs
@@ -156,7 +156,7 @@ namespace Umbraco.Core.BackOffice
///
///
///
- public async Task UpdateAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
+ public Task UpdateAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@@ -192,7 +192,7 @@ namespace Umbraco.Core.BackOffice
}
}
- return IdentityResult.Success;
+ return Task.FromResult(IdentityResult.Success);
}
///
diff --git a/src/Umbraco.Infrastructure/BackOffice/IBackOfficeUserManager.cs b/src/Umbraco.Infrastructure/BackOffice/IBackOfficeUserManager.cs
index ee61359a70..664c957f57 100644
--- a/src/Umbraco.Infrastructure/BackOffice/IBackOfficeUserManager.cs
+++ b/src/Umbraco.Infrastructure/BackOffice/IBackOfficeUserManager.cs
@@ -311,13 +311,14 @@ namespace Umbraco.Core.BackOffice
///
Task GetPhoneNumberAsync(TUser user);
+ // TODO: These are raised from outside the signinmanager and usermanager in the auth and user controllers,
+ // let's see if there's a way to avoid that and only have these called within signinmanager and usermanager
+ // which means we can remove these from the interface (things like invite seems like they cannot be moved)
void RaiseForgotPasswordRequestedEvent(IPrincipal currentUser, int userId);
void RaiseForgotPasswordChangedSuccessEvent(IPrincipal currentUser, int userId);
SignOutAuditEventArgs RaiseLogoutSuccessEvent(IPrincipal currentUser, int userId);
UserInviteEventArgs RaiseSendingUserInvite(IPrincipal currentUser, UserInvite invite, IUser createdUser);
- void RaiseLoginSuccessEvent(TUser currentUser, int userId);
-
bool HasSendingUserInviteEventHandler { get; }
}
}
diff --git a/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs b/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs
index 5f258bcb87..78d5d5554c 100644
--- a/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs
+++ b/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs
@@ -18,6 +18,7 @@ using Umbraco.Tests.Common.Builders;
using Umbraco.Web.BackOffice.Controllers;
using Umbraco.Web.BackOffice.Routing;
using Umbraco.Web.Common.Install;
+using Umbraco.Web.Common.Security;
using Umbraco.Web.WebApi;
namespace Umbraco.Tests.UnitTests.AutoFixture
diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs
index 2d9c66806b..0eaa141250 100644
--- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs
+++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs
@@ -111,7 +111,6 @@ namespace Umbraco.Tests.Testing.TestingTests
var memberService = Mock.Of();
var memberTypeService = Mock.Of();
var membershipProvider = new MembersMembershipProvider(memberService, memberTypeService, Mock.Of(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver());
- var membershipHelper = new MembershipHelper(Mock.Of(), Mock.Of(), membershipProvider, Mock.Of(), memberService, memberTypeService, Mock.Of(), AppCaches.Disabled, NullLoggerFactory.Instance, ShortStringHelper, Mock.Of());
var umbracoMapper = new UmbracoMapper(new MapDefinitionCollection(new[] { Mock.Of() }));
var umbracoApiController = new FakeUmbracoApiController(new GlobalSettings(), Mock.Of(), Mock.Of(), Mock.Of(), ServiceContext.CreatePartial(), AppCaches.NoCache, profilingLogger , Mock.Of(), umbracoMapper, Mock.Of());
diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
index 6aa6c46eee..5c95be2afc 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
@@ -5,6 +5,7 @@ using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
@@ -66,6 +67,7 @@ namespace Umbraco.Web.BackOffice.Controllers
private readonly IRequestAccessor _requestAccessor;
private readonly LinkGenerator _linkGenerator;
private readonly IBackOfficeExternalLoginProviders _externalAuthenticationOptions;
+ private readonly IBackOfficeTwoFactorOptions _backOfficeTwoFactorOptions;
// TODO: We need to import the logic from Umbraco.Web.Editors.AuthenticationController
// TODO: We need to review all _userManager.Raise calls since many/most should be on the usermanager or signinmanager, very few should be here
@@ -87,7 +89,8 @@ namespace Umbraco.Web.BackOffice.Controllers
Core.Hosting.IHostingEnvironment hostingEnvironment,
IRequestAccessor requestAccessor,
LinkGenerator linkGenerator,
- IBackOfficeExternalLoginProviders externalAuthenticationOptions)
+ IBackOfficeExternalLoginProviders externalAuthenticationOptions,
+ IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions)
{
_backofficeSecurityAccessor = backofficeSecurityAccessor;
_userManager = backOfficeUserManager;
@@ -106,6 +109,7 @@ namespace Umbraco.Web.BackOffice.Controllers
_requestAccessor = requestAccessor;
_linkGenerator = linkGenerator;
_externalAuthenticationOptions = externalAuthenticationOptions;
+ _backOfficeTwoFactorOptions = backOfficeTwoFactorOptions;
}
///
@@ -303,7 +307,7 @@ namespace Umbraco.Web.BackOffice.Controllers
///
[SetAngularAntiForgeryTokens]
[DenyLocalLoginAuthorization]
- public async Task PostLogin(LoginModel loginModel)
+ public async Task> PostLogin(LoginModel loginModel)
{
// Sign the user in with username/password, this also gives a chance for developers to
// custom verify the credentials and auto-link user accounts with a custom IBackOfficePasswordChecker
@@ -318,40 +322,25 @@ namespace Umbraco.Web.BackOffice.Controllers
if (result.RequiresTwoFactor)
{
- throw new NotImplementedException("Implement MFA/2FA, we need to have some IOptions or similar to configure this");
+ var twofactorView = _backOfficeTwoFactorOptions.GetTwoFactorView(loginModel.Username);
+ if (twofactorView.IsNullOrWhiteSpace())
+ {
+ return new ValidationErrorResult($"The registered {typeof(IBackOfficeTwoFactorOptions)} of type {_backOfficeTwoFactorOptions.GetType()} did not return a view for two factor auth ");
+ }
- //var twofactorOptions = UserManager as IUmbracoBackOfficeTwoFactorOptions;
- //if (twofactorOptions == null)
- //{
- // throw new HttpResponseException(
- // Request.CreateErrorResponse(
- // HttpStatusCode.BadRequest,
- // "UserManager does not implement " + typeof(IUmbracoBackOfficeTwoFactorOptions)));
- //}
+ var attemptedUser = _userService.GetByUsername(loginModel.Username);
- //var twofactorView = twofactorOptions.GetTwoFactorView(
- // owinContext,
- // UmbracoContext,
- // loginModel.Username);
+ // create a with information to display a custom two factor send code view
+ var verifyResponse = new ObjectResult(new
+ {
+ twoFactorView = twofactorView,
+ userId = attemptedUser.Id
+ })
+ {
+ StatusCode = StatusCodes.Status402PaymentRequired
+ };
- //if (twofactorView.IsNullOrWhiteSpace())
- //{
- // throw new HttpResponseException(
- // Request.CreateErrorResponse(
- // HttpStatusCode.BadRequest,
- // typeof(IUmbracoBackOfficeTwoFactorOptions) + ".GetTwoFactorView returned an empty string"));
- //}
-
- //var attemptedUser = Services.UserService.GetByUsername(loginModel.Username);
-
- //// create a with information to display a custom two factor send code view
- //var verifyResponse = Request.CreateResponse(HttpStatusCode.PaymentRequired, new
- //{
- // twoFactorView = twofactorView,
- // userId = attemptedUser.Id
- //});
-
- //_userManager.RaiseLoginRequiresVerificationEvent(User, attemptedUser.Id);
+
//return verifyResponse;
}
diff --git a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs
index 1b8e6fb80a..8d3223a79f 100644
--- a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs
+++ b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs
@@ -63,6 +63,7 @@ namespace Umbraco.Extensions
services.TryAddScoped();
services.TryAddScoped();
services.TryAddSingleton();
+ services.TryAddSingleton();
/*
* IdentityBuilderExtensions.AddUserManager adds UserManager to service collection
diff --git a/src/Umbraco.Web.BackOffice/Security/AuthenticationBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/Security/AuthenticationBuilderExtensions.cs
new file mode 100644
index 0000000000..e2a7aeccaf
--- /dev/null
+++ b/src/Umbraco.Web.BackOffice/Security/AuthenticationBuilderExtensions.cs
@@ -0,0 +1,15 @@
+using System;
+using Umbraco.Core.Builder;
+
+namespace Umbraco.Web.BackOffice.Security
+{
+ public static class AuthenticationBuilderExtensions
+ {
+ public static IUmbracoBuilder AddBackOfficeExternalLogins(this IUmbracoBuilder umbracoBuilder, Action builder)
+ {
+ builder(new BackOfficeExternalLoginsBuilder(umbracoBuilder.Services));
+ return umbracoBuilder;
+ }
+ }
+
+}
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs
new file mode 100644
index 0000000000..b3418697e2
--- /dev/null
+++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeAuthenticationBuilder.cs
@@ -0,0 +1,64 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Options;
+using System;
+using Umbraco.Core;
+
+namespace Umbraco.Web.BackOffice.Security
+{
+ ///
+ /// Custom used to associate external logins with umbraco external login options
+ ///
+ public class BackOfficeAuthenticationBuilder : AuthenticationBuilder
+ {
+ private readonly BackOfficeExternalLoginProviderOptions _loginProviderOptions;
+
+ public BackOfficeAuthenticationBuilder(IServiceCollection services, BackOfficeExternalLoginProviderOptions loginProviderOptions)
+ : base(services)
+ {
+ _loginProviderOptions = loginProviderOptions;
+ }
+
+ public string SchemeForBackOffice(string scheme)
+ {
+ return Constants.Security.BackOfficeExternalAuthenticationTypePrefix + scheme;
+ }
+
+ ///
+ /// Overridden to track the final authenticationScheme being registered for the external login
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override AuthenticationBuilder AddRemoteScheme(string authenticationScheme, string displayName, Action configureOptions)
+ {
+ // Validate that the prefix is set
+ if (!authenticationScheme.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix))
+ {
+ throw new InvalidOperationException($"The {nameof(authenticationScheme)} is not prefixed with {Constants.Security.BackOfficeExternalAuthenticationTypePrefix}. The scheme must be created with a call to the method {nameof(SchemeForBackOffice)}");
+ }
+
+ // add our login provider to the container along with a custom options configuration
+ Services.AddSingleton(x => new BackOfficeExternalLoginProvider(displayName, authenticationScheme, _loginProviderOptions));
+ Services.TryAddEnumerable(ServiceDescriptor.Singleton, EnsureBackOfficeScheme>());
+
+ return base.AddRemoteScheme(authenticationScheme, displayName, configureOptions);
+ }
+
+ // TODO: We could override and throw NotImplementedException for other methods?
+
+ // Ensures that the sign in scheme is always the Umbraco back office external type
+ private class EnsureBackOfficeScheme : IPostConfigureOptions where TOptions : RemoteAuthenticationOptions
+ {
+ public void PostConfigure(string name, TOptions options)
+ {
+ options.SignInScheme = Constants.Security.BackOfficeExternalAuthenticationType;
+ }
+ }
+ }
+
+}
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs
new file mode 100644
index 0000000000..18e5b066dc
--- /dev/null
+++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProvider.cs
@@ -0,0 +1,39 @@
+using System;
+
+namespace Umbraco.Web.BackOffice.Security
+{
+ ///
+ /// An external login (OAuth) provider for the back office
+ ///
+ public class BackOfficeExternalLoginProvider : IEquatable
+ {
+ public BackOfficeExternalLoginProvider(string name, string authenticationType, BackOfficeExternalLoginProviderOptions properties)
+ {
+ Name = name ?? throw new ArgumentNullException(nameof(name));
+ AuthenticationType = authenticationType ?? throw new ArgumentNullException(nameof(authenticationType));
+ Options = properties ?? throw new ArgumentNullException(nameof(properties));
+ }
+
+ public string Name { get; }
+ public string AuthenticationType { get; }
+ public BackOfficeExternalLoginProviderOptions Options { get; }
+
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as BackOfficeExternalLoginProvider);
+ }
+
+ public bool Equals(BackOfficeExternalLoginProvider other)
+ {
+ return other != null &&
+ Name == other.Name &&
+ AuthenticationType == other.AuthenticationType;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Name, AuthenticationType);
+ }
+ }
+
+}
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs
new file mode 100644
index 0000000000..08ef32dc57
--- /dev/null
+++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviders.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Umbraco.Web.BackOffice.Security
+{
+ public class BackOfficeExternalLoginProviders : IBackOfficeExternalLoginProviders
+ {
+ public BackOfficeExternalLoginProviders(IEnumerable externalLogins)
+ {
+ _externalLogins = externalLogins;
+ }
+
+ private readonly IEnumerable _externalLogins;
+
+ public BackOfficeExternalLoginProvider Get(string authenticationType)
+ {
+ return _externalLogins.FirstOrDefault(x => x.AuthenticationType == authenticationType);
+ }
+
+ public string GetAutoLoginProvider()
+ {
+ var found = _externalLogins.Where(x => x.Options.AutoRedirectLoginToExternalProvider).ToList();
+ return found.Count > 0 ? found[0].AuthenticationType : null;
+ }
+
+ public IEnumerable GetBackOfficeProviders()
+ {
+ return _externalLogins;
+ }
+
+ public bool HasDenyLocalLogin()
+ {
+ var found = _externalLogins.Where(x => x.Options.DenyLocalLogin).ToList();
+ return found.Count > 0;
+ }
+ }
+
+}
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginsBuilder.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginsBuilder.cs
new file mode 100644
index 0000000000..402ad8b948
--- /dev/null
+++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginsBuilder.cs
@@ -0,0 +1,33 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+
+namespace Umbraco.Web.BackOffice.Security
+{
+ ///
+ /// Used to add back office login providers
+ ///
+ public class BackOfficeExternalLoginsBuilder
+ {
+ public BackOfficeExternalLoginsBuilder(IServiceCollection services)
+ {
+ _services = services;
+ }
+
+ private readonly IServiceCollection _services;
+
+ ///
+ /// Add a back office login provider with options
+ ///
+ ///
+ ///
+ ///
+ public BackOfficeExternalLoginsBuilder AddBackOfficeLogin(
+ BackOfficeExternalLoginProviderOptions loginProviderOptions,
+ Action build)
+ {
+ build(new BackOfficeAuthenticationBuilder(_services, loginProviderOptions));
+ return this;
+ }
+ }
+
+}
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs
index f08be0bd99..d2050e214f 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs
+++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs
@@ -10,8 +10,11 @@ using System.Security.Claims;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.BackOffice;
+using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
+using Umbraco.Core.Security;
using Umbraco.Extensions;
+using Umbraco.Net;
using Umbraco.Web.BackOffice.Security;
namespace Umbraco.Web.Common.Security
@@ -436,16 +439,17 @@ namespace Umbraco.Web.Common.Security
Logger.LogInformation("User: {UserName} logged in from IP address {IpAddress}", username, Context.Connection.RemoteIpAddress);
if (user != null)
{
- _userManager.RaiseLoginSuccessEvent(user, user.Id);
+ _userManager.RaiseLoginSuccessEvent(Context.User, user.Id);
}
}
else if (result.IsLockedOut)
{
- _userManager.RaiseAccountLockedEvent(user, user.Id);
+ _userManager.RaiseAccountLockedEvent(Context.User, user.Id);
Logger.LogInformation("Login attempt failed for username {UserName} from IP address {IpAddress}, the user is locked", username, Context.Connection.RemoteIpAddress);
}
else if (result.RequiresTwoFactor)
{
+ _userManager.RaiseLoginRequiresVerificationEvent(Context.User, user.Id);
Logger.LogInformation("Login attempt requires verification for username {UserName} from IP address {IpAddress}", username, Context.Connection.RemoteIpAddress);
}
else if (!result.Succeeded || result.IsNotAllowed)
diff --git a/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManager.cs
similarity index 86%
rename from src/Umbraco.Infrastructure/BackOffice/BackOfficeUserManager.cs
rename to src/Umbraco.Web.BackOffice/Security/BackOfficeUserManager.cs
index 1e5cd8436e..3501242eb5 100644
--- a/src/Umbraco.Infrastructure/BackOffice/BackOfficeUserManager.cs
+++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManager.cs
@@ -1,11 +1,14 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Umbraco.Core;
+using Umbraco.Core.BackOffice;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Models.Membership;
@@ -14,7 +17,8 @@ using Umbraco.Extensions;
using Umbraco.Net;
using Umbraco.Web.Models.ContentEditing;
-namespace Umbraco.Core.BackOffice
+
+namespace Umbraco.Web.Common.Security
{
public class BackOfficeUserManager : BackOfficeUserManager, IBackOfficeUserManager
{
@@ -28,9 +32,10 @@ namespace Umbraco.Core.BackOffice
BackOfficeLookupNormalizer keyNormalizer,
BackOfficeIdentityErrorDescriber errors,
IServiceProvider services,
+ IHttpContextAccessor httpContextAccessor,
ILogger> logger,
IOptions passwordConfiguration)
- : base(ipResolver, store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger, passwordConfiguration)
+ : base(ipResolver, store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, httpContextAccessor, logger, passwordConfiguration)
{
}
}
@@ -39,6 +44,7 @@ namespace Umbraco.Core.BackOffice
where T : BackOfficeIdentityUser
{
private PasswordGenerator _passwordGenerator;
+ private readonly IHttpContextAccessor _httpContextAccessor;
public BackOfficeUserManager(
IIpResolver ipResolver,
@@ -50,11 +56,13 @@ namespace Umbraco.Core.BackOffice
BackOfficeLookupNormalizer keyNormalizer,
BackOfficeIdentityErrorDescriber errors,
IServiceProvider services,
+ IHttpContextAccessor httpContextAccessor,
ILogger> logger,
IOptions passwordConfiguration)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
IpResolver = ipResolver ?? throw new ArgumentNullException(nameof(ipResolver));
+ _httpContextAccessor = httpContextAccessor;
PasswordConfiguration = passwordConfiguration.Value ?? throw new ArgumentNullException(nameof(passwordConfiguration));
}
@@ -95,7 +103,7 @@ namespace Umbraco.Core.BackOffice
{
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;
+ if (userSessionStore == null) return true;
return await userSessionStore.ValidateSessionIdAsync(userId, sessionId);
}
@@ -140,7 +148,7 @@ namespace Umbraco.Core.BackOffice
{
if (user == null) throw new ArgumentNullException(nameof(user));
- if (user.IsApproved == false) return true;
+ if (user.IsApproved == false) return true;
return await base.IsLockedOutAsync(user);
}
@@ -211,7 +219,9 @@ namespace Umbraco.Core.BackOffice
var result = await base.ResetPasswordAsync(user, token, newPassword);
if (result.Succeeded)
- RaisePasswordChangedEvent(null, userId); // TODO: How can we get the current user? we have not HttpContext (netstandard), we can make our own IPrincipalAccessor?
+ {
+ RaisePasswordChangedEvent(_httpContextAccessor.HttpContext?.User, userId);
+ }
return result;
}
@@ -219,7 +229,9 @@ namespace Umbraco.Core.BackOffice
{
var result = await base.ChangePasswordAsync(user, currentPassword, newPassword);
if (result.Succeeded)
- RaisePasswordChangedEvent(null, user.Id); // TODO: How can we get the current user? we have not HttpContext (netstandard), we can make our own IPrincipalAccessor?
+ {
+ RaisePasswordChangedEvent(_httpContextAccessor.HttpContext?.User, user.Id);
+ }
return result;
}
@@ -297,11 +309,11 @@ namespace Umbraco.Core.BackOffice
// The way we unlock is by setting the lockoutEnd date to the current datetime
if (result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow)
{
- RaiseAccountLockedEvent(null, user.Id); // TODO: How can we get the current user? we have not HttpContext (netstandard), we can make our own IPrincipalAccessor?
+ RaiseAccountLockedEvent(_httpContextAccessor.HttpContext?.User, user.Id);
}
else
{
- RaiseAccountUnlockedEvent(null, user.Id); // TODO: How can we get the current user? we have not HttpContext (netstandard), we can make our own IPrincipalAccessor?
+ RaiseAccountUnlockedEvent(_httpContextAccessor.HttpContext?.User, user.Id);
//Resets the login attempt fails back to 0 when unlock is clicked
await ResetAccessFailedCountAsync(user);
}
@@ -321,7 +333,7 @@ namespace Umbraco.Core.BackOffice
await lockoutStore.ResetAccessFailedCountAsync(user, CancellationToken.None);
//raise the event now that it's reset
- RaiseResetAccessFailedCountEvent(null, user.Id); // TODO: How can we get the current user? we have not HttpContext (netstandard), we can make our own IPrincipalAccessor?
+ RaiseResetAccessFailedCountEvent(_httpContextAccessor.HttpContext?.User, user.Id);
return await UpdateAsync(user);
}
@@ -357,8 +369,7 @@ namespace Umbraco.Core.BackOffice
//Slightly confusing: this will return a Success if we successfully update the AccessFailed count
if (result.Succeeded)
{
- // TODO: This may no longer be the case in netcore, we'll need to see about that
- RaiseLoginFailedEvent(null, user.Id); // TODO: How can we get the current user? we have not HttpContext (netstandard), we can make our own IPrincipalAccessor?
+ RaiseLoginFailedEvent(_httpContextAccessor.HttpContext?.User, user.Id);
}
return result;
@@ -367,7 +378,7 @@ namespace Umbraco.Core.BackOffice
private int GetCurrentUserId(IPrincipal currentUser)
{
var umbIdentity = currentUser?.GetUmbracoIdentity();
- var currentUserId = umbIdentity?.GetUserId() ?? Constants.Security.SuperUserId;
+ var currentUserId = umbIdentity?.GetUserId() ?? Core.Constants.Security.SuperUserId;
return currentUserId;
}
private IdentityAuditEventArgs CreateArgs(AuditEvent auditEvent, IPrincipal currentUser, int affectedUserId, string affectedUsername)
@@ -383,9 +394,9 @@ namespace Umbraco.Core.BackOffice
return new IdentityAuditEventArgs(auditEvent, ip, currentUserId, string.Empty, affectedUserId, affectedUsername);
}
- // TODO: Review where these are raised and see if they can be simplified and either done in the this usermanager or the signin manager, lastly we'll resort to the authentication controller
- // In some cases it will be nicer/easier to not pass in IPrincipal
- public void RaiseAccountLockedEvent(BackOfficeIdentityUser currentUser, int userId) => OnAccountLocked(CreateArgs(AuditEvent.AccountLocked, currentUser, userId, string.Empty));
+ // TODO: Review where these are raised and see if they can be simplified and either done in the this usermanager or the signin manager,
+ // lastly we'll resort to the authentication controller but we should try to remove all instances of that occuring
+ public void RaiseAccountLockedEvent(IPrincipal currentUser, int userId) => OnAccountLocked(CreateArgs(AuditEvent.AccountLocked, currentUser, userId, string.Empty));
public void RaiseAccountUnlockedEvent(IPrincipal currentUser, int userId) => OnAccountUnlocked(CreateArgs(AuditEvent.AccountUnlocked, currentUser, userId, string.Empty));
@@ -395,11 +406,11 @@ namespace Umbraco.Core.BackOffice
public void RaiseLoginFailedEvent(IPrincipal currentUser, int userId) => OnLoginFailed(CreateArgs(AuditEvent.LoginFailed, currentUser, userId, string.Empty));
- public void RaiseInvalidLoginAttemptEvent(IPrincipal currentUser, string username) => OnLoginFailed(CreateArgs(AuditEvent.LoginFailed, currentUser, Constants.Security.SuperUserId, username));
+ //public void RaiseInvalidLoginAttemptEvent(IPrincipal currentUser, string username) => OnLoginFailed(CreateArgs(AuditEvent.LoginFailed, currentUser, Constants.Security.SuperUserId, username));
public void RaiseLoginRequiresVerificationEvent(IPrincipal currentUser, int userId) => OnLoginRequiresVerification(CreateArgs(AuditEvent.LoginRequiresVerification, currentUser, userId, string.Empty));
- public void RaiseLoginSuccessEvent(BackOfficeIdentityUser currentUser, int userId) => OnLoginSuccess(CreateArgs(AuditEvent.LoginSucces, currentUser, userId, string.Empty));
+ public void RaiseLoginSuccessEvent(IPrincipal currentUser, int userId) => OnLoginSuccess(CreateArgs(AuditEvent.LoginSucces, currentUser, userId, string.Empty));
public SignOutAuditEventArgs RaiseLogoutSuccessEvent(IPrincipal currentUser, int userId)
{
@@ -408,7 +419,7 @@ namespace Umbraco.Core.BackOffice
OnLogoutSuccess(args);
return args;
}
-
+
public void RaisePasswordChangedEvent(IPrincipal currentUser, int userId) => OnPasswordChanged(CreateArgs(AuditEvent.LogoutSuccess, currentUser, userId, string.Empty));
public void RaiseResetAccessFailedCountEvent(IPrincipal currentUser, int userId) => OnResetAccessFailedCount(CreateArgs(AuditEvent.ResetAccessFailedCount, currentUser, userId, string.Empty));
@@ -424,17 +435,17 @@ namespace Umbraco.Core.BackOffice
public bool HasSendingUserInviteEventHandler => SendingUserInvite != null;
- 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;
+ public event EventHandler AccountLocked;
+ public event EventHandler AccountUnlocked;
+ public event EventHandler ForgotPasswordRequested;
+ public event EventHandler ForgotPasswordChangedSuccess;
+ public event EventHandler LoginFailed;
+ public event EventHandler LoginRequiresVerification;
+ public event EventHandler LoginSuccess;
+ public event EventHandler LogoutSuccess;
+ public event EventHandler PasswordChanged;
+ public event EventHandler PasswordReset;
+ public event EventHandler ResetAccessFailedCount;
///
/// Raised when a user is invited
diff --git a/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs b/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs
index 6b78e58ead..2274df14a3 100644
--- a/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs
+++ b/src/Umbraco.Web.BackOffice/Security/IBackOfficeExternalLoginProviders.cs
@@ -1,111 +1,14 @@
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Authentication.OAuth;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Options;
-using System;
+using Microsoft.AspNetCore.Authentication.OAuth;
using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Linq;
using System.Text;
-using Umbraco.Core;
-using Umbraco.Core.Builder;
namespace Umbraco.Web.BackOffice.Security
{
- ///
- /// Custom used to associate external logins with umbraco external login options
- ///
- public class BackOfficeAuthenticationBuilder : AuthenticationBuilder
- {
- private readonly BackOfficeExternalLoginProviderOptions _loginProviderOptions;
-
- public BackOfficeAuthenticationBuilder(IServiceCollection services, BackOfficeExternalLoginProviderOptions loginProviderOptions)
- : base(services)
- {
- _loginProviderOptions = loginProviderOptions;
- }
-
- public string SchemeForBackOffice(string scheme)
- {
- return Constants.Security.BackOfficeExternalAuthenticationTypePrefix + scheme;
- }
-
- ///
- /// Overridden to track the final authenticationScheme being registered for the external login
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public override AuthenticationBuilder AddRemoteScheme(string authenticationScheme, string displayName, Action configureOptions)
- {
- // Validate that the prefix is set
- if (!authenticationScheme.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix))
- {
- throw new InvalidOperationException($"The {nameof(authenticationScheme)} is not prefixed with {Constants.Security.BackOfficeExternalAuthenticationTypePrefix}. The scheme must be created with a call to the method {nameof(SchemeForBackOffice)}");
- }
-
- // add our login provider to the container along with a custom options configuration
- Services.AddSingleton(x => new BackOfficeExternalLoginProvider(displayName, authenticationScheme, _loginProviderOptions));
- Services.TryAddEnumerable(ServiceDescriptor.Singleton, EnsureBackOfficeScheme>());
-
- return base.AddRemoteScheme(authenticationScheme, displayName, configureOptions);
- }
-
- // TODO: We could override and throw NotImplementedException for other methods?
-
- // Ensures that the sign in scheme is always the Umbraco back office external type
- private class EnsureBackOfficeScheme : IPostConfigureOptions where TOptions : RemoteAuthenticationOptions
- {
- public void PostConfigure(string name, TOptions options)
- {
- options.SignInScheme = Constants.Security.BackOfficeExternalAuthenticationType;
- }
- }
- }
///
- /// Used to add back office login providers
+ /// Service to return instances
///
- public class BackOfficeExternalLoginsBuilder
- {
- public BackOfficeExternalLoginsBuilder(IServiceCollection services)
- {
- _services = services;
- }
-
- private readonly IServiceCollection _services;
-
- ///
- /// Add a back office login provider with options
- ///
- ///
- ///
- ///
- public BackOfficeExternalLoginsBuilder AddBackOfficeLogin(
- BackOfficeExternalLoginProviderOptions loginProviderOptions,
- Action build)
- {
- build(new BackOfficeAuthenticationBuilder(_services, loginProviderOptions));
- return this;
- }
- }
-
- public static class AuthenticationBuilderExtensions
- {
- public static IUmbracoBuilder AddBackOfficeExternalLogins(this IUmbracoBuilder umbracoBuilder, Action builder)
- {
- builder(new BackOfficeExternalLoginsBuilder(umbracoBuilder.Services));
- return umbracoBuilder;
- }
- }
-
- // TODO: We need to implement this and extend it to support the back office external login options
- // basically migrate things from AuthenticationManagerExtensions & AuthenticationOptionsExtensions
- // and use this to get the back office external login infos
public interface IBackOfficeExternalLoginProviders
{
BackOfficeExternalLoginProvider Get(string authenticationType);
@@ -126,67 +29,4 @@ namespace Umbraco.Web.BackOffice.Security
bool HasDenyLocalLogin();
}
- public class BackOfficeExternalLoginProviders : IBackOfficeExternalLoginProviders
- {
- public BackOfficeExternalLoginProviders(IEnumerable externalLogins)
- {
- _externalLogins = externalLogins;
- }
-
- private readonly IEnumerable _externalLogins;
-
- public BackOfficeExternalLoginProvider Get(string authenticationType)
- {
- return _externalLogins.FirstOrDefault(x => x.AuthenticationType == authenticationType);
- }
-
- public string GetAutoLoginProvider()
- {
- var found = _externalLogins.Where(x => x.Options.AutoRedirectLoginToExternalProvider).ToList();
- return found.Count > 0 ? found[0].AuthenticationType : null;
- }
-
- public IEnumerable GetBackOfficeProviders()
- {
- return _externalLogins;
- }
-
- public bool HasDenyLocalLogin()
- {
- var found = _externalLogins.Where(x => x.Options.DenyLocalLogin).ToList();
- return found.Count > 0;
- }
- }
-
- public class BackOfficeExternalLoginProvider : IEquatable
- {
- public BackOfficeExternalLoginProvider(string name, string authenticationType, BackOfficeExternalLoginProviderOptions properties)
- {
- Name = name ?? throw new ArgumentNullException(nameof(name));
- AuthenticationType = authenticationType ?? throw new ArgumentNullException(nameof(authenticationType));
- Options = properties ?? throw new ArgumentNullException(nameof(properties));
- }
-
- public string Name { get; }
- public string AuthenticationType { get; }
- public BackOfficeExternalLoginProviderOptions Options { get; }
-
- public override bool Equals(object obj)
- {
- return Equals(obj as BackOfficeExternalLoginProvider);
- }
-
- public bool Equals(BackOfficeExternalLoginProvider other)
- {
- return other != null &&
- Name == other.Name &&
- AuthenticationType == other.AuthenticationType;
- }
-
- public override int GetHashCode()
- {
- return HashCode.Combine(Name, AuthenticationType);
- }
- }
-
}
diff --git a/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs b/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs
new file mode 100644
index 0000000000..a05d71f3cb
--- /dev/null
+++ b/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs
@@ -0,0 +1,16 @@
+namespace Umbraco.Web.BackOffice.Security
+{
+ ///
+ /// Options used to control 2FA for the Umbraco back office
+ ///
+ public interface IBackOfficeTwoFactorOptions
+ {
+ ///
+ /// Returns the angular view for handling 2FA interaction
+ ///
+ ///
+ ///
+ string GetTwoFactorView(string username);
+ }
+
+}
diff --git a/src/Umbraco.Web.BackOffice/Security/NoopBackOfficeTwoFactorOptions.cs b/src/Umbraco.Web.BackOffice/Security/NoopBackOfficeTwoFactorOptions.cs
new file mode 100644
index 0000000000..bbc0b3e049
--- /dev/null
+++ b/src/Umbraco.Web.BackOffice/Security/NoopBackOfficeTwoFactorOptions.cs
@@ -0,0 +1,8 @@
+namespace Umbraco.Web.BackOffice.Security
+{
+ public class NoopBackOfficeTwoFactorOptions : IBackOfficeTwoFactorOptions
+ {
+ public string GetTwoFactorView(string username) => null;
+ }
+
+}
diff --git a/src/Umbraco.Web/Compose/BackOfficeUserAuditEventsComponent.cs b/src/Umbraco.Web/Compose/BackOfficeUserAuditEventsComponent.cs
index bfb80924d1..d8210556fd 100644
--- a/src/Umbraco.Web/Compose/BackOfficeUserAuditEventsComponent.cs
+++ b/src/Umbraco.Web/Compose/BackOfficeUserAuditEventsComponent.cs
@@ -10,6 +10,7 @@ using Umbraco.Web.Security;
namespace Umbraco.Web.Compose
{
+ // TODO: Move to netcore
public sealed class BackOfficeUserAuditEventsComponent : IComponent
{
private readonly IAuditService _auditService;
@@ -27,14 +28,14 @@ namespace Umbraco.Web.Compose
{
//BackOfficeUserManager.AccountLocked += ;
//BackOfficeUserManager.AccountUnlocked += ;
- BackOfficeOwinUserManager.ForgotPasswordRequested += OnForgotPasswordRequest;
- BackOfficeOwinUserManager.ForgotPasswordChangedSuccess += OnForgotPasswordChange;
- BackOfficeOwinUserManager.LoginFailed += OnLoginFailed;
+ //BackOfficeOwinUserManager.ForgotPasswordRequested += OnForgotPasswordRequest;
+ //BackOfficeOwinUserManager.ForgotPasswordChangedSuccess += OnForgotPasswordChange;
+ //BackOfficeOwinUserManager.LoginFailed += OnLoginFailed;
//BackOfficeUserManager.LoginRequiresVerification += ;
- BackOfficeOwinUserManager.LoginSuccess += OnLoginSuccess;
- BackOfficeOwinUserManager.LogoutSuccess += OnLogoutSuccess;
- BackOfficeOwinUserManager.PasswordChanged += OnPasswordChanged;
- BackOfficeOwinUserManager.PasswordReset += OnPasswordReset;
+ //BackOfficeOwinUserManager.LoginSuccess += OnLoginSuccess;
+ //BackOfficeOwinUserManager.LogoutSuccess += OnLogoutSuccess;
+ //BackOfficeOwinUserManager.PasswordChanged += OnPasswordChanged;
+ //BackOfficeOwinUserManager.PasswordReset += OnPasswordReset;
//BackOfficeUserManager.ResetAccessFailedCount += ;
}
@@ -42,14 +43,14 @@ namespace Umbraco.Web.Compose
{
//BackOfficeUserManager.AccountLocked -= ;
//BackOfficeUserManager.AccountUnlocked -= ;
- BackOfficeOwinUserManager.ForgotPasswordRequested -= OnForgotPasswordRequest;
- BackOfficeOwinUserManager.ForgotPasswordChangedSuccess -= OnForgotPasswordChange;
- BackOfficeOwinUserManager.LoginFailed -= OnLoginFailed;
+ //BackOfficeOwinUserManager.ForgotPasswordRequested -= OnForgotPasswordRequest;
+ //BackOfficeOwinUserManager.ForgotPasswordChangedSuccess -= OnForgotPasswordChange;
+ //BackOfficeOwinUserManager.LoginFailed -= OnLoginFailed;
//BackOfficeUserManager.LoginRequiresVerification -= ;
- BackOfficeOwinUserManager.LoginSuccess -= OnLoginSuccess;
- BackOfficeOwinUserManager.LogoutSuccess -= OnLogoutSuccess;
- BackOfficeOwinUserManager.PasswordChanged -= OnPasswordChanged;
- BackOfficeOwinUserManager.PasswordReset -= OnPasswordReset;
+ //BackOfficeOwinUserManager.LoginSuccess -= OnLoginSuccess;
+ //BackOfficeOwinUserManager.LogoutSuccess -= OnLogoutSuccess;
+ //BackOfficeOwinUserManager.PasswordChanged -= OnPasswordChanged;
+ //BackOfficeOwinUserManager.PasswordReset -= OnPasswordReset;
//BackOfficeUserManager.ResetAccessFailedCount -= ;
}
diff --git a/src/Umbraco.Web/Editors/PasswordChanger.cs b/src/Umbraco.Web/Editors/PasswordChanger.cs
deleted file mode 100644
index be6a20e7cc..0000000000
--- a/src/Umbraco.Web/Editors/PasswordChanger.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Umbraco.Core;
-using Umbraco.Core.Models;
-using Umbraco.Extensions;
-using Umbraco.Web.Models;
-using Umbraco.Web.Security;
-using IUser = Umbraco.Core.Models.Membership.IUser;
-
-//Migrated to .NET CORE
-namespace Umbraco.Web.Editors
-{
- internal class PasswordChanger
- {
- private readonly ILogger _logger;
-
- public PasswordChanger(ILogger logger)
- {
- _logger = logger;
- }
-
- ///
- /// Changes the password for a user based on the many different rules and config options
- ///
- /// The user performing the password save action
- /// The user who's password is being changed
- ///
- ///
- ///
- public async Task> ChangePasswordWithIdentityAsync(
- IUser currentUser,
- IUser savingUser,
- ChangingPasswordModel passwordModel,
- BackOfficeOwinUserManager userMgr)
- {
- if (passwordModel == null) throw new ArgumentNullException(nameof(passwordModel));
- if (userMgr == null) throw new ArgumentNullException(nameof(userMgr));
-
- if (passwordModel.NewPassword.IsNullOrWhiteSpace())
- {
- return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Cannot set an empty password", new[] { "value" }) });
- }
-
- var backOfficeIdentityUser = await userMgr.FindByIdAsync(savingUser.Id.ToString());
- if (backOfficeIdentityUser == null)
- {
- //this really shouldn't ever happen... but just in case
- return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password could not be verified", new[] { "oldPassword" }) });
- }
-
- //Are we just changing another user's password?
- if (passwordModel.OldPassword.IsNullOrWhiteSpace())
- {
- //if it's the current user, the current user cannot reset their own password
- if (currentUser.Username == savingUser.Username)
- {
- return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password reset is not allowed", new[] { "value" }) });
- }
-
- //if the current user has access to reset/manually change the password
- if (currentUser.HasSectionAccess(Umbraco.Core.Constants.Applications.Users) == false)
- {
- return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("The current user is not authorized", new[] { "value" }) });
- }
-
- //ok, we should be able to reset it
- var resetToken = await userMgr.GeneratePasswordResetTokenAsync(backOfficeIdentityUser);
-
- var resetResult = await userMgr.ChangePasswordWithResetAsync(savingUser.Id, resetToken, passwordModel.NewPassword);
-
- if (resetResult.Succeeded == false)
- {
- var errors = resetResult.Errors.ToErrorMessage();
- _logger.LogWarning("Could not reset user password {PasswordErrors}", errors);
- return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult(errors, new[] { "value" }) });
- }
-
- return Attempt.Succeed(new PasswordChangedModel());
- }
-
- //is the old password correct?
- var validateResult = await userMgr.CheckPasswordAsync(backOfficeIdentityUser, passwordModel.OldPassword);
- if (validateResult == false)
- {
- //no, fail with an error message for "oldPassword"
- return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Incorrect password", new[] { "oldPassword" }) });
- }
- //can we change to the new password?
- var changeResult = await userMgr.ChangePasswordAsync(backOfficeIdentityUser, passwordModel.OldPassword, passwordModel.NewPassword);
- if (changeResult.Succeeded == false)
- {
- //no, fail with error messages for "password"
- var errors = changeResult.Errors.ToErrorMessage();
- _logger.LogWarning("Could not change user password {PasswordErrors}", errors);
- return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult(errors, new[] { "password" }) });
- }
- return Attempt.Succeed(new PasswordChangedModel());
- }
-
- }
-}
diff --git a/src/Umbraco.Web/OwinExtensions.cs b/src/Umbraco.Web/OwinExtensions.cs
index 62f1643074..801ceae191 100644
--- a/src/Umbraco.Web/OwinExtensions.cs
+++ b/src/Umbraco.Web/OwinExtensions.cs
@@ -52,25 +52,6 @@ namespace Umbraco.Web
return ctx == null ? Attempt.Fail() : Attempt.Succeed(ctx);
}
-
- ///
- /// Gets the back office user manager out of OWIN
- ///
- ///
- ///
- ///
- /// 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 BackOfficeOwinUserManager GetBackOfficeUserManager(this IOwinContext owinContext)
- {
- 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 (BackOfficeOwinUserManager)} from the {typeof (IOwinContext)}.");
- }
-
///
/// Adapted from Microsoft.AspNet.Identity.Owin.OwinContextExtensions
///
diff --git a/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs b/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs
deleted file mode 100644
index 2f5e858687..0000000000
--- a/src/Umbraco.Web/Security/BackOfficeOwinUserManager.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-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.Models;
-using Umbraco.Core.Mapping;
-using Umbraco.Core.Services;
-using Umbraco.Net;
-
-namespace Umbraco.Web.Security
-{
- // TODO: Most of this is already migrated to netcore, there's probably not much more to go and then we can complete remove it
- public class BackOfficeOwinUserManager : BackOfficeUserManager
- {
- public const string OwinMarkerKey = "Umbraco.Web.Security.Identity.BackOfficeUserManagerMarker";
-
- public BackOfficeOwinUserManager(
- IOptions passwordConfiguration,
- IIpResolver ipResolver,
- IUserStore store,
- IOptions optionsAccessor,
- IEnumerable> userValidators,
- IEnumerable> passwordValidators,
- BackOfficeLookupNormalizer keyNormalizer,
- BackOfficeIdentityErrorDescriber errors,
- IDataProtectionProvider dataProtectionProvider,
- ILogger> logger)
- : base(ipResolver, store, optionsAccessor, null, userValidators, passwordValidators, keyNormalizer, errors, null, logger, passwordConfiguration)
- {
- PasswordConfiguration = passwordConfiguration.Value;
- 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,
- IOptions globalSettings,
- UmbracoMapper mapper,
- IOptions passwordConfiguration,
- IIpResolver ipResolver,
- BackOfficeIdentityErrorDescriber 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(
- IOptions passwordConfiguration,
- IIpResolver ipResolver,
- IUserStore customUserStore,
- BackOfficeIdentityErrorDescriber errors,
- IDataProtectionProvider dataProtectionProvider,
- ILogger> logger)
- {
- var options = new BackOfficeIdentityOptions();
-
- // 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.Value.RequiredLength;
- options.Password.RequireNonAlphanumeric = passwordConfiguration.Value.RequireNonLetterOrDigit;
- options.Password.RequireDigit = passwordConfiguration.Value.RequireDigit;
- options.Password.RequireLowercase = passwordConfiguration.Value.RequireLowercase;
- options.Password.RequireUppercase = passwordConfiguration.Value.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.Security.SecurityStampClaimType;
-
- options.Lockout.AllowedForNewUsers = true;
- options.Lockout.MaxFailedAccessAttempts = passwordConfiguration.Value.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 BackOfficeLookupNormalizer(),
- 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/BackOfficeUserManagerMarker.cs b/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs
deleted file mode 100644
index dd657b48bf..0000000000
--- a/src/Umbraco.Web/Security/BackOfficeUserManagerMarker.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using Microsoft.Owin;
-using Umbraco.Core.BackOffice;
-
-namespace Umbraco.Web.Security
-{
- ///
- /// This class is only here due to the fact that IOwinContext Get / Set only work in generics, if they worked
- /// with regular 'object' then we wouldn't have to use this work around but because of that we have to use this
- /// class to resolve the 'real' type of the registered user manager
- ///
- ///
- ///
- internal class BackOfficeUserManagerMarker : IBackOfficeUserManagerMarker
- where TManager : BackOfficeUserManager
- where TUser : BackOfficeIdentityUser
- {
- public BackOfficeOwinUserManager GetManager(IOwinContext owin)
- {
- 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/IBackOfficeUserManagerMarker.cs b/src/Umbraco.Web/Security/IBackOfficeUserManagerMarker.cs
deleted file mode 100644
index 16c0666c9c..0000000000
--- a/src/Umbraco.Web/Security/IBackOfficeUserManagerMarker.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Microsoft.Owin;
-
-namespace Umbraco.Web.Security
-{
- ///
- /// This interface is only here due to the fact that IOwinContext Get / Set only work in generics, if they worked
- /// with regular 'object' then we wouldn't have to use this work around but because of that we have to use this
- /// class to resolve the 'real' type of the registered user manager
- ///
- internal interface IBackOfficeUserManagerMarker
- {
- BackOfficeOwinUserManager GetManager(IOwinContext owin);
- }
-}
diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs
index 1e26782d4a..65251320ca 100644
--- a/src/Umbraco.Web/Security/MembershipHelper.cs
+++ b/src/Umbraco.Web/Security/MembershipHelper.cs
@@ -18,9 +18,7 @@ using Umbraco.Web.Security.Providers;
namespace Umbraco.Web.Security
{
- ///
- /// A helper class for handling Members
- ///
+ // MIGRATED TO NETCORE
public class MembershipHelper
{
private readonly MembersMembershipProvider _membershipProvider;
@@ -680,37 +678,6 @@ namespace Umbraco.Web.Security
return allowAction;
}
- ///
- /// Changes password for a member/user given the membership provider name and the password change model
- ///
- ///
- ///
- ///
- ///
- public virtual Attempt ChangePassword(string username, ChangingPasswordModel passwordModel, string membershipProviderName)
- {
- var provider = Membership.Providers[membershipProviderName];
- if (provider == null)
- {
- throw new InvalidOperationException("Could not find provider with name " + membershipProviderName);
- }
-
- return ChangePassword(username, passwordModel, provider);
- }
-
- ///
- /// Changes password for a member/user given the membership provider and the password change model
- ///
- ///
- ///
- ///
- ///
- public virtual Attempt ChangePassword(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider)
- {
- var passwordChanger = new PasswordChanger(_loggerFactory.CreateLogger());
- return ChangePasswordWithMembershipProvider(username, passwordModel, membershipProvider);
- }
-
///
/// Updates a membership user with all of it's writable properties
///
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 1b4ec244d8..7a2ec6e0f7 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -137,7 +137,6 @@
-
@@ -164,8 +163,6 @@
-
-
@@ -181,7 +178,6 @@
-
diff --git a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs
index f744213276..37e552d818 100644
--- a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs
+++ b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs
@@ -31,8 +31,6 @@ namespace Umbraco.Web.WebApi
[EnableDetailedErrors]
public abstract class UmbracoAuthorizedApiController : UmbracoApiController
{
- private BackOfficeOwinUserManager _userManager;
-
protected UmbracoAuthorizedApiController()
{
}
@@ -42,10 +40,5 @@ namespace Umbraco.Web.WebApi
{
}
- ///
- /// Gets the user manager.
- ///
- protected BackOfficeOwinUserManager UserManager
- => _userManager ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager());
}
}