add dialog for change password, shift form from user details view int… (#7450)

This commit is contained in:
Nathan Woulfe
2020-01-27 15:29:04 +10:00
committed by Kenn Jacobsen
parent ebed80791d
commit 902d4196cf
9 changed files with 161 additions and 60 deletions
@@ -405,6 +405,43 @@
formattedSaveData),
"Failed to save user");
}
/**
* @ngdoc method
* @name umbraco.resources.usersResource#changePassword
* @methodOf umbraco.resources.usersResource
*
* @description
* Changes a user's password
*
* ##usage
* <pre>
* usersResource.changePassword(changePasswordModel)
* .then(function() {
* // password changed
* });
* </pre>
*
* @param {Object} model object to save
* @returns {Promise} resourcePromise object containing the updated user.
*
*/
function changePassword(changePasswordModel) {
if (!changePasswordModel) {
throw "password model not specified";
}
//need to convert the password data into the correctly formatted save data - it is *not* the same and we don't want to over-post
var formattedPasswordData = umbDataFormatter.formatChangePasswordModel(changePasswordModel);
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"userApiBaseUrl",
"PostChangePassword"),
formattedPasswordData),
"Failed to save user");
}
/**
* @ngdoc method
@@ -447,6 +484,7 @@
createUser: createUser,
inviteUser: inviteUser,
saveUser: saveUser,
changePassword: changePassword,
deleteNonLoggedInUser: deleteNonLoggedInUser,
clearAvatar: clearAvatar
};
@@ -152,8 +152,7 @@
formatUserPostData: function (displayModel) {
//create the save model from the display model
var saveModel = _.pick(displayModel, 'id', 'parentId', 'name', 'username', 'culture', 'email', 'startContentIds', 'startMediaIds', 'userGroups', 'message', 'changePassword');
saveModel.changePassword = this.formatChangePasswordModel(saveModel.changePassword);
var saveModel = _.pick(displayModel, 'id', 'parentId', 'name', 'username', 'culture', 'email', 'startContentIds', 'startMediaIds', 'userGroups', 'message');
//make sure the userGroups are just a string array
var currGroups = saveModel.userGroups;
@@ -0,0 +1,6 @@
<ng-form name="passwordForm" class="block-form" val-form-manager>
<change-password password-values="model.changePassword" config="model.config">
</change-password>
</ng-form>
@@ -21,7 +21,8 @@
//create the initial model for change password
vm.changePasswordModel = {
config: {},
isChanging: false
isChanging: false,
value: {}
};
vm.goToPage = goToPage;
@@ -37,7 +38,8 @@
vm.changeAvatar = changeAvatar;
vm.clearAvatar = clearAvatar;
vm.save = save;
vm.changePassword = changePassword;
vm.toggleChangePassword = toggleChangePassword;
function init() {
@@ -125,23 +127,34 @@
}
function toggleChangePassword() {
vm.changePasswordModel.isChanging = !vm.changePasswordModel.isChanging;
//reset it
vm.user.changePassword = null;
//reset it
vm.user.changePassword = null;
localizationService.localizeMany(["general_cancel", "general_confirm", "general_changePassword"])
.then(function (data) {
const overlay = {
view: "changepassword",
title: data[2],
changePassword: vm.user.changePassword,
config: vm.changePasswordModel.config,
closeButtonLabel: data[0],
submitButtonLabel: data[1],
submitButtonStyle: 'success',
close: () => overlayService.close(),
submit: model => {
overlayService.close();
vm.changePasswordModel.value = model.changePassword;
changePassword();
}
};
overlayService.open(overlay);
});
}
function save() {
if (formHelper.submitForm({ scope: $scope })) {
//anytime a user is changing another user's password, we are in effect resetting it so we need to set that flag here
if (vm.user.changePassword) {
//NOTE: the check for allowManuallyChangingPassword is due to this legacy user membership provider setting, if that is true, then the current user
//can change their own password without entering their current one (this is a legacy setting since that is a security issue but we need to maintain compat).
//if allowManuallyChangingPassword=false, then we are using default settings and the user will need to enter their old password to change their own password.
vm.user.changePassword.reset = (!vm.user.changePassword.oldPassword && !vm.user.isCurrentUser) || vm.changePasswordModel.config.allowManuallyChangingPassword;
}
vm.page.saveButtonState = "busy";
vm.user.resetPasswordValue = null;
@@ -161,11 +174,7 @@
//restore
vm.user.navigation = currentNav;
setUserDisplayState();
formatDatesToLocal(vm.user);
vm.changePasswordModel.isChanging = false;
//the user has a password if they are not states: Invited, NoCredentials
vm.changePasswordModel.config.hasPassword = vm.user.userState !== 3 && vm.user.userState !== 4;
formatDatesToLocal(vm.user);
vm.page.saveButtonState = "success";
@@ -181,6 +190,36 @@
}
}
/**
*
*/
function changePassword() {
//anytime a user is changing another user's password, we are in effect resetting it so we need to set that flag here
if (vm.changePasswordModel.value) {
//NOTE: the check for allowManuallyChangingPassword is due to this legacy user membership provider setting, if that is true, then the current user
//can change their own password without entering their current one (this is a legacy setting since that is a security issue but we need to maintain compat).
//if allowManuallyChangingPassword=false, then we are using default settings and the user will need to enter their old password to change their own password.
vm.changePasswordModel.value.reset = (!vm.changePasswordModel.value.oldPassword && !vm.user.isCurrentUser) || vm.changePasswordModel.config.allowManuallyChangingPassword;
}
// since we don't send the entire user model, the id is required
vm.changePasswordModel.value.id = vm.user.id;
usersResource.changePassword(vm.changePasswordModel.value)
.then(() => {
vm.changePasswordModel.isChanging = false;
vm.changePasswordModel.value = {};
//the user has a password if they are not states: Invited, NoCredentials
vm.changePasswordModel.config.hasPassword = vm.user.userState !== 3 && vm.user.userState !== 4;
}, err => {
contentEditingHelper.handleSaveError({
err: err,
showNotifications: true
});
});
}
/**
* Used to emit the save event and await any async operations being performed by editor extensions
* @param {any} savedUser
@@ -258,7 +258,7 @@
</div>
<div>
<umb-button type="button" ng-if="model.user.userDisplayState.key !== 'Invited' && model.changePasswordModel.isChanging === false"
<umb-button type="button" ng-if="model.user.userDisplayState.key !== 'Invited'"
button-style="[action,block]"
action="model.toggleChangePassword()"
label="Change password"
@@ -278,21 +278,6 @@
</umb-button>
</div>
<ng-form ng-if="model.changePasswordModel.isChanging" name="passwordForm" class="block-form" val-form-manager>
<change-password password-values="model.user.changePassword"
config="model.changePasswordModel.config">
</change-password>
<umb-button type="button"
action="model.toggleChangePassword()"
label="Cancel"
label-key="general_cancel"
button-style="cancel">
</umb-button>
</ng-form>
<div ng-if="model.user.resetPasswordValue">
<p><br />Password reset to value: <strong>{{model.user.resetPasswordValue}}</strong></p>
</div>
@@ -737,6 +737,7 @@
<key alias="sort">Sort</key>
<key alias="status">Status</key>
<key alias="submit">Submit</key>
<key alias="success">Success</key>
<key alias="type">Type</key>
<key alias="typeToSearch">Type to search...</key>
<key alias="under">under</key>
@@ -1843,7 +1844,8 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="password">Password</key>
<key alias="resetPassword">Reset password</key>
<key alias="passwordChanged">Your password has been changed!</key>
<key alias="passwordConfirm">Please confirm the new password</key>
<key alias="passwordChangedGeneric">Password changed</key>
<key alias="passwordConfirm">Please confirm the new password</key>
<key alias="passwordEnterNew">Enter your new password</key>
<key alias="passwordIsBlank">Your new password cannot be blank!</key>
<key alias="passwordCurrent">Current password</key>
@@ -738,6 +738,7 @@
<key alias="sort">Sort</key>
<key alias="status">Status</key>
<key alias="submit">Submit</key>
<key alias="success">Success</key>
<key alias="type">Type</key>
<key alias="typeToSearch">Type to search...</key>
<key alias="under">under</key>
@@ -1852,6 +1853,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="password">Password</key>
<key alias="resetPassword">Reset password</key>
<key alias="passwordChanged">Your password has been changed!</key>
<key alias="passwordChangedGeneric">Password changed</key>
<key alias="passwordConfirm">Please confirm the new password</key>
<key alias="passwordEnterNew">Enter your new password</key>
<key alias="passwordIsBlank">Your new password cannot be blank!</key>
+47 -23
View File
@@ -26,6 +26,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Editors.Filters;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
@@ -549,29 +550,7 @@ namespace Umbraco.Web.Editors
if (Current.Configs.Settings().Security.UsernameIsEmail && found.Username == found.Email && userSave.Username != userSave.Email)
{
userSave.Username = userSave.Email;
}
if (userSave.ChangePassword != null)
{
var passwordChanger = new PasswordChanger(Logger, Services.UserService, UmbracoContext.HttpContext);
//this will change the password and raise appropriate events
var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, userSave.ChangePassword, UserManager);
if (passwordChangeResult.Success)
{
//need to re-get the user
found = Services.UserService.GetUserById(intId.Result);
}
else
{
hasErrors = true;
foreach (var memberName in passwordChangeResult.Result.ChangeError.MemberNames)
{
ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage);
}
}
}
}
if (hasErrors)
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
@@ -587,6 +566,51 @@ namespace Umbraco.Web.Editors
return display;
}
/// <summary>
///
/// </summary>
/// <param name="changingPasswordModel"></param>
/// <returns></returns>
public async Task<ModelWithNotifications<string>> PostChangePassword(ChangingPasswordModel changingPasswordModel)
{
changingPasswordModel = changingPasswordModel ?? throw new ArgumentNullException(nameof(changingPasswordModel));
if (ModelState.IsValid == false)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
}
var intId = changingPasswordModel.Id.TryConvertTo<int>();
if (intId.Success == false)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var found = Services.UserService.GetUserById(intId.Result);
if (found == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var passwordChanger = new PasswordChanger(Logger, Services.UserService, UmbracoContext.HttpContext);
var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, changingPasswordModel, UserManager);
if (passwordChangeResult.Success)
{
var result = new ModelWithNotifications<string>(passwordChangeResult.Result.ResetPassword);
result.AddSuccessNotification(Services.TextService.Localize("general/success"), Services.TextService.Localize("user/passwordChangedGeneric"));
return result;
}
foreach (var memberName in passwordChangeResult.Result.ChangeError.MemberNames)
{
ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage);
}
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
/// <summary>
/// Disables the users with the given user ids
/// </summary>
@@ -49,5 +49,11 @@ namespace Umbraco.Web.Models
/// </summary>
[DataMember(Name = "generatedPassword")]
public string GeneratedPassword { get; set; }
/// <summary>
/// The id of the user - required to allow changing password without the entire UserSave model
/// </summary>
[DataMember(Name = "id")]
public int Id { get; set; }
}
}