From ae17172126dbd016e353ca3db1c503268dec482c Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Sat, 29 Apr 2017 12:49:56 +0200 Subject: [PATCH] Attempt to raise events from parts of code that don't hit the BackOfficeUserManager directly --- .../Auditing/IdentityAuditEventArgs.cs | 9 ++-- .../Security/BackOfficeUserManager.cs | 50 ++++++++++++++++++- .../Editors/AuthenticationController.cs | 18 +++++++ src/Umbraco.Web/Security/MembershipHelper.cs | 27 ++++++++++ .../Providers/UmbracoMembershipProvider.cs | 15 +++++- 5 files changed, 111 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Auditing/IdentityAuditEventArgs.cs b/src/Umbraco.Core/Auditing/IdentityAuditEventArgs.cs index 27e6dc69c3..6746aff8f2 100644 --- a/src/Umbraco.Core/Auditing/IdentityAuditEventArgs.cs +++ b/src/Umbraco.Core/Auditing/IdentityAuditEventArgs.cs @@ -1,7 +1,5 @@ using System; using System.Web; -using Microsoft.AspNet.Identity; -using Microsoft.AspNet.Identity.Owin; using Umbraco.Core.Security; namespace Umbraco.Core.Auditing @@ -61,11 +59,12 @@ namespace Umbraco.Core.Auditing AccountLocked, AccountUnlocked, LoginSucces, - Logout, + LogoutSuccess, AccessFailed, PasswordChanged, - AccountCreated, + AccountCreated, //not yet being called ResetAccessFailedCount, - AccountUpdated + AccountUpdated, + PasswordReset } } diff --git a/src/Umbraco.Core/Security/BackOfficeUserManager.cs b/src/Umbraco.Core/Security/BackOfficeUserManager.cs index 8f84aa1ee8..7c3139d323 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserManager.cs @@ -238,7 +238,6 @@ namespace Umbraco.Core.Security /// public IBackOfficeUserPasswordChecker BackOfficeUserPasswordChecker { get; set; } - //TODO: Call this method (?) public override Task SetLockoutEnabledAsync(int userId, bool enabled) { var result = base.SetLockoutEnabledAsync(userId, enabled); @@ -265,7 +264,6 @@ namespace Umbraco.Core.Security return result; } - //TODO: Call this method (?) public override Task ChangePasswordAsync(int userId, string currentPassword, string newPassword) { var result = base.ChangePasswordAsync(userId, currentPassword, newPassword); @@ -315,6 +313,54 @@ namespace Umbraco.Core.Security return await Task.FromResult(IdentityResult.Success); } + internal void RaisePasswordChangedEvent(int userId) + { + OnAuthResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.PasswordChanged) + { + AffectedUser = userId + }); + } + + internal void RaisePasswordResetEvent(int userId) + { + OnAuthResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.PasswordReset) + { + AffectedUser = userId + }); + } + + internal void RaiseAccountLockedEvent(int userId) + { + OnAuthResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.AccountLocked) + { + AffectedUser = userId + }); + } + + internal void RaiseResetAccessFailedCountEvent(int userId) + { + OnAuthResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.ResetAccessFailedCount) + { + AffectedUser = userId + }); + } + + internal void RaiseLoginSuccessEvent(int userId) + { + OnAuthResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.LoginSucces) + { + AffectedUser = userId + }); + } + + internal void RaiseLogoutSuccessEvent(int userId) + { + OnAuthResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.LogoutSuccess) + { + AffectedUser = userId + }); + } + public override Task UpdateAsync(T user) { var result = base.UpdateAsync(user); diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index 0489fcbb70..398657e63d 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -327,9 +327,27 @@ namespace Umbraco.Web.Editors () => User.Identity == null ? "UNKNOWN" : User.Identity.Name, () => TryGetOwinContext().Result.Request.RemoteIpAddress); + var backofficeUserManager = GetBackofficeUserManager(); + if (backofficeUserManager != null) + { + var userId = -1; + int.TryParse(User.Identity.GetUserId(), out userId); + backofficeUserManager.RaiseLogoutSuccessEvent(userId); + } + return Request.CreateResponse(HttpStatusCode.OK); } + internal BackOfficeUserManager GetBackofficeUserManager() + { + + if (HttpContext.Current == null) return null; + var owinContext = HttpContext.Current.GetOwinContext(); + if (owinContext == null) return null; + var userManager = owinContext.GetBackOfficeUserManager(); + if (userManager == null) return null; + return userManager; + } /// /// This is used when the user is auth'd successfully and we need to return an OK with user details along with setting the current Principal in the request diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index 605b5137d8..95019b3e21 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Security; using Umbraco.Web.Models; using Umbraco.Web.PublishedCache; using Umbraco.Core.Cache; +using Umbraco.Core.Models.Identity; using Umbraco.Web.Security.Providers; using MPE = global::Umbraco.Core.Security.MembershipProviderExtensions; @@ -660,6 +661,15 @@ namespace Umbraco.Web.Security if (passwordModel == null) throw new ArgumentNullException("passwordModel"); if (membershipProvider == null) throw new ArgumentNullException("membershipProvider"); + int userId = -1; + var backofficeUserManager = GetBackofficeUserManager(); + if (backofficeUserManager != null) + { + var profile = _applicationContext.Services.UserService.GetProfileByUserName(username); + if (profile != null) + int.TryParse(profile.Id.ToString(), out userId); + } + //Are we resetting the password?? if (passwordModel.Reset.HasValue && passwordModel.Reset.Value) { @@ -679,6 +689,9 @@ namespace Umbraco.Web.Security username, membershipProvider.RequiresQuestionAndAnswer ? passwordModel.Answer : null); + if (backofficeUserManager != null && userId >= 0) + backofficeUserManager.RaisePasswordResetEvent(userId); + //return the generated pword return Attempt.Succeed(new PasswordChangedModel { ResetPassword = newPass }); } @@ -729,6 +742,10 @@ namespace Umbraco.Web.Security try { var result = membershipProvider.ChangePassword(username, passwordModel.OldPassword, passwordModel.NewPassword); + + if (result && backofficeUserManager != null && userId >= 0) + backofficeUserManager.RaisePasswordChangedEvent(userId); + return result == false ? Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid username or password", new[] { "value" }) }) : Attempt.Succeed(new PasswordChangedModel()); @@ -870,5 +887,15 @@ namespace Umbraco.Web.Security return sb.ToString(); } + internal BackOfficeUserManager GetBackofficeUserManager() + { + + if (HttpContext.Current == null) return null; + var owinContext = HttpContext.Current.GetOwinContext(); + if (owinContext == null) return null; + var userManager = owinContext.GetBackOfficeUserManager(); + if (userManager == null) return null; + return userManager; + } } } diff --git a/src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs index bbd1548710..a624aa8703 100644 --- a/src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs @@ -551,6 +551,7 @@ namespace Umbraco.Web.Security.Providers } var authenticated = CheckPassword(password, member.RawPasswordValue); + var backofficeUserManager = GetBackofficeUserManager(); if (authenticated == false) { @@ -570,6 +571,9 @@ namespace Umbraco.Web.Security.Providers "Login attempt failed for username {0} from IP address {1}, the user is now locked out, max invalid password attempts exceeded", username, GetCurrentRequestIpAddress())); + + if(backofficeUserManager != null) + backofficeUserManager.RaiseAccountLockedEvent(member.Id); } else { @@ -582,7 +586,13 @@ namespace Umbraco.Web.Security.Providers } else { - member.FailedPasswordAttempts = 0; + if (member.FailedPasswordAttempts > 0) + { + member.FailedPasswordAttempts = 0; + if (backofficeUserManager != null) + backofficeUserManager.RaiseResetAccessFailedCountEvent(member.Id); + } + member.LastLoginDate = DateTime.Now; LogHelper.Info( @@ -590,6 +600,9 @@ namespace Umbraco.Web.Security.Providers "Login attempt succeeded for username {0} from IP address {1}", username, GetCurrentRequestIpAddress())); + + if (backofficeUserManager != null) + backofficeUserManager.RaiseLoginSuccessEvent(member.Id); } //don't raise events for this! It just sets the member dates, if we do raise events this will