From a0306f9d550d69c5aab60778b4034ac3ffa362f9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 4 Sep 2017 20:15:46 +1000 Subject: [PATCH] Fixes ability to reset your own password in the user section --- .../UmbracoSettings/ISecuritySection.cs | 5 +++- .../UmbracoSettings/SecurityElement.cs | 6 ++++ .../Security/MembershipProviderExtensions.cs | 3 ++ .../views/common/dialogs/user.controller.js | 2 +- .../src/views/users/user.controller.js | 3 ++ .../Editors/CurrentUserController.cs | 2 +- src/Umbraco.Web/Editors/PasswordChanger.cs | 28 +++++++++++++++---- src/Umbraco.Web/Editors/UsersController.cs | 2 +- 8 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs index c44c0cf0df..4cc9ba402a 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs @@ -5,7 +5,10 @@ bool KeepUserLoggedIn { get; } bool HideDisabledUsersInBackoffice { get; } - + + /// + /// Used to enable/disable the forgot password functionality on the back office login screen + /// bool AllowPasswordReset { get; } string AuthCookieName { get; } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs index ddb168ddbd..1918d740ba 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs @@ -16,6 +16,9 @@ namespace Umbraco.Core.Configuration.UmbracoSettings get { return GetOptionalTextElement("hideDisabledUsersInBackoffice", false); } } + /// + /// Used to enable/disable the forgot password functionality on the back office login screen + /// [ConfigurationProperty("allowPasswordReset")] internal InnerTextConfigurationElement AllowPasswordReset { @@ -44,6 +47,9 @@ namespace Umbraco.Core.Configuration.UmbracoSettings get { return HideDisabledUsersInBackoffice; } } + /// + /// Used to enable/disable the forgot password functionality on the back office login screen + /// bool ISecuritySection.AllowPasswordReset { get { return AllowPasswordReset; } diff --git a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs index 7f787050d4..c423c1a94a 100644 --- a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs +++ b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs @@ -18,6 +18,9 @@ namespace Umbraco.Core.Security /// /// /// + /// + /// An Admin can always reset the password + /// internal static bool CanResetPassword(this MembershipProvider provider, IUserService userService) { if (provider == null) throw new ArgumentNullException("provider"); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js index cc7f1d158f..e7444b8119 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js @@ -119,7 +119,7 @@ angular.module("umbraco") $scope.changePasswordModel.config = data; //ensure the hasPassword config option is set to true (the user of course has a password already assigned) //this will ensure the oldPassword is shown so they can change it - // disable reset password functionality beacuse it does not make sense inside the backoffice + // disable reset password functionality beacuse it does not make sense for the current user. $scope.changePasswordModel.config.hasPassword = true; $scope.changePasswordModel.config.disableToggle = true; $scope.changePasswordModel.config.enableReset = false; diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js index 89b1334baf..47ced5c9fc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js @@ -78,6 +78,9 @@ vm.changePasswordModel.config.hasPassword = vm.user.userState !== 3 && vm.user.userState !== 4; vm.changePasswordModel.config.disableToggle = true; + + //if it's the current user then disable password reset since that doesn't make sense. + vm.changePasswordModel.config.enableReset = !vm.user.isCurrentUser; vm.loading = false; }); diff --git a/src/Umbraco.Web/Editors/CurrentUserController.cs b/src/Umbraco.Web/Editors/CurrentUserController.cs index 1fcf87c313..f2f3a7cc03 100644 --- a/src/Umbraco.Web/Editors/CurrentUserController.cs +++ b/src/Umbraco.Web/Editors/CurrentUserController.cs @@ -90,7 +90,7 @@ namespace Umbraco.Web.Editors public async Task> PostChangePassword(ChangingPasswordModel data) { var passwordChanger = new PasswordChanger(Logger, Services.UserService); - var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, data, ModelState, UserManager); + var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, Security.CurrentUser, data, ModelState, UserManager); if (passwordChangeResult.Success) { diff --git a/src/Umbraco.Web/Editors/PasswordChanger.cs b/src/Umbraco.Web/Editors/PasswordChanger.cs index 17fb9eeb19..7b29274366 100644 --- a/src/Umbraco.Web/Editors/PasswordChanger.cs +++ b/src/Umbraco.Web/Editors/PasswordChanger.cs @@ -27,9 +27,19 @@ namespace Umbraco.Web.Editors _logger = logger; _userService = userService; } - + + /// + /// 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, ModelStateDictionary modelState, BackOfficeUserManager userMgr) @@ -48,7 +58,7 @@ namespace Umbraco.Web.Editors //if this isn't using an IUserAwarePasswordHasher, then fallback to the old way if (membershipPasswordHasher.MembershipProvider.RequiresQuestionAndAnswer) throw new NotSupportedException("Currently the user editor does not support providers that have RequiresQuestionAndAnswer specified"); - return ChangePasswordWithMembershipProvider(currentUser.Username, passwordModel, membershipPasswordHasher.MembershipProvider); + return ChangePasswordWithMembershipProvider(savingUser.Username, passwordModel, membershipPasswordHasher.MembershipProvider); } //if we are here, then a IUserAwarePasswordHasher is available, however we cannot proceed in that case if for some odd reason @@ -62,10 +72,16 @@ namespace Umbraco.Web.Editors //Are we resetting the password?? if (passwordModel.Reset.HasValue && passwordModel.Reset.Value) { + //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[] { "resetPassword" }) }); + } + //ok, we should be able to reset it - var resetToken = await userMgr.GeneratePasswordResetTokenAsync(currentUser.Id); + var resetToken = await userMgr.GeneratePasswordResetTokenAsync(savingUser.Id); var newPass = userMgr.GeneratePassword(); - var resetResult = await userMgr.ResetPasswordAsync(currentUser.Id, resetToken, newPass); + var resetResult = await userMgr.ResetPasswordAsync(savingUser.Id, resetToken, newPass); if (resetResult.Succeeded == false) { @@ -95,7 +111,7 @@ namespace Umbraco.Web.Editors if (passwordModel.OldPassword.IsNullOrWhiteSpace() == false) { //if an old password is suplied try to change it - var changeResult = await userMgr.ChangePasswordAsync(currentUser.Id, passwordModel.OldPassword, passwordModel.NewPassword); + var changeResult = await userMgr.ChangePasswordAsync(savingUser.Id, passwordModel.OldPassword, passwordModel.NewPassword); if (changeResult.Succeeded == false) { var errors = string.Join(". ", changeResult.Errors); @@ -112,7 +128,7 @@ namespace Umbraco.Web.Editors /// /// Changes password for a member/user given the membership provider and the password change model /// - /// + /// The username of the user having their password changed /// /// /// diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index 65eb0054e6..055105290e 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -522,7 +522,7 @@ namespace Umbraco.Web.Editors { var passwordChanger = new PasswordChanger(Logger, Services.UserService); - var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(found, userSave.ChangePassword, ModelState, UserManager); + var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, userSave.ChangePassword, ModelState, UserManager); if (passwordChangeResult.Success) { //depending on how the provider is configured, the password may be reset so let's store that for later