From 7becf76a0242178f8748b3b55f235d14266940d3 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 20 Apr 2022 10:56:15 +0200 Subject: [PATCH] Added notification when requires user 2fa, so implementors can use this to send emails etc. --- .../UserTwoFactorRequestedNotification.cs | 14 ++++++ .../Services/UserServiceExtensions.cs | 6 +++ .../Security/BackOfficeSignInManager.cs | 50 ++++++++++++++++++- 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Notifications/UserTwoFactorRequestedNotification.cs diff --git a/src/Umbraco.Core/Notifications/UserTwoFactorRequestedNotification.cs b/src/Umbraco.Core/Notifications/UserTwoFactorRequestedNotification.cs new file mode 100644 index 0000000000..ccb07c593c --- /dev/null +++ b/src/Umbraco.Core/Notifications/UserTwoFactorRequestedNotification.cs @@ -0,0 +1,14 @@ +using System; + +namespace Umbraco.Cms.Core.Notifications +{ + public class UserTwoFactorRequestedNotification : INotification + { + public UserTwoFactorRequestedNotification(Guid userKey) + { + UserKey = userKey; + } + + public Guid UserKey { get; } + } +} diff --git a/src/Umbraco.Core/Services/UserServiceExtensions.cs b/src/Umbraco.Core/Services/UserServiceExtensions.cs index 57d09077fc..19f1a7ac5c 100644 --- a/src/Umbraco.Core/Services/UserServiceExtensions.cs +++ b/src/Umbraco.Core/Services/UserServiceExtensions.cs @@ -84,5 +84,11 @@ namespace Umbraco.Extensions }); } + + public static IUser GetByKey(this IUserService userService, Guid key) + { + int id = BitConverter.ToInt32(key.ToByteArray(), 0); + return userService.GetUserById(id); + } } } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs index ec0d273b56..dc71c5f6bb 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs @@ -6,10 +6,14 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Cms.Web.Common.Security; using Umbraco.Extensions; @@ -24,6 +28,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security { private readonly BackOfficeUserManager _userManager; private readonly IBackOfficeExternalLoginProviders _externalLogins; + private readonly IEventAggregator _eventAggregator; private readonly GlobalSettings _globalSettings; protected override string AuthenticationType => Constants.Security.BackOfficeAuthenticationType; @@ -43,14 +48,32 @@ namespace Umbraco.Cms.Web.BackOffice.Security IOptions globalSettings, ILogger> logger, IAuthenticationSchemeProvider schemes, - IUserConfirmation confirmation) + IUserConfirmation confirmation, + IEventAggregator eventAggregator) : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation) { _userManager = userManager; _externalLogins = externalLogins; + _eventAggregator = eventAggregator; _globalSettings = globalSettings.Value; } + [Obsolete("Use ctor with all params")] + public BackOfficeSignInManager( + BackOfficeUserManager userManager, + IHttpContextAccessor contextAccessor, + IBackOfficeExternalLoginProviders externalLogins, + IUserClaimsPrincipalFactory claimsFactory, + IOptions optionsAccessor, + IOptions globalSettings, + ILogger> logger, + IAuthenticationSchemeProvider schemes, + IUserConfirmation confirmation) + : this(userManager, contextAccessor, externalLogins, claimsFactory, optionsAccessor, globalSettings, logger, schemes, confirmation, StaticServiceProvider.Instance.GetRequiredService()) + { + + } + /// /// Custom ExternalLoginSignInAsync overload for handling external sign in with auto-linking /// @@ -284,6 +307,31 @@ namespace Umbraco.Cms.Web.BackOffice.Security } } + protected override async Task SignInOrTwoFactorAsync(BackOfficeIdentityUser user, bool isPersistent, + string loginProvider = null, bool bypassTwoFactor = false) + { + var result = await base.SignInOrTwoFactorAsync(user, isPersistent, loginProvider, bypassTwoFactor); + + if (result.RequiresTwoFactor) + { + NotifyRequiresTwoFactor(user); + } + + return result; + } + + protected void NotifyRequiresTwoFactor(BackOfficeIdentityUser user) => Notify(user, + (currentUser) => new UserTwoFactorRequestedNotification(currentUser.Key) + ); + + private T Notify(BackOfficeIdentityUser currentUser, Func createNotification) where T : INotification + { + + var notification = createNotification(currentUser); + _eventAggregator.Publish(notification); + return notification; + } + private void LogFailedExternalLogin(ExternalLoginInfo loginInfo, BackOfficeIdentityUser user) => Logger.LogWarning("The AutoLinkOptions of the external authentication provider '{LoginProvider}' have refused the login based on the OnExternalLogin method. Affected user id: '{UserId}'", loginInfo.LoginProvider, user.Id); }