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