-
- Password has been reset to:
-
- {{model.value.generatedPassword}}
-
-
+
+
+
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 2d28ee8cd8..f241ae9244 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
@@ -1,13 +1,15 @@
(function () {
"use strict";
- function UserEditController($scope, $timeout, $location, $routeParams, usersResource, contentEditingHelper, localizationService, notificationsService, mediaHelper, Upload, umbRequestHelper, usersHelper) {
+ function UserEditController($scope, $timeout, $location, $routeParams, usersResource, contentEditingHelper, localizationService, notificationsService, mediaHelper, Upload, umbRequestHelper, usersHelper, authResource) {
var vm = this;
var localizeSaving = localizationService.localize("general_saving");
vm.page = {};
- vm.user = {};
+ vm.user = {
+ changePassword: null
+ };
vm.breadcrumbs = [];
vm.avatarFile = {};
@@ -23,6 +25,13 @@
vm.save = save;
vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB"
vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
+ vm.toggleChangePassword = toggleChangePassword;
+
+ //create the initial model for change password
+ vm.changePasswordModel = {
+ config: {},
+ isChanging: false
+ };
function init() {
@@ -36,6 +45,22 @@
vm.loading = false;
});
+ //go get the config for the membership provider and add it to the model
+ authResource.getMembershipProviderConfig().then(function (data) {
+ vm.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
+ vm.changePasswordModel.config.hasPassword = true;
+ vm.changePasswordModel.config.disableToggle = true;
+ vm.changePasswordModel.config.enableReset = false;
+ });
+ }
+
+ function toggleChangePassword() {
+ vm.changePasswordModel.isChanging = !vm.changePasswordModel.isChanging;
+ //reset it
+ vm.user.changePassword = null;
}
function save() {
@@ -56,6 +81,7 @@
vm.user = saved;
setUserDisplayState();
+ vm.changePasswordModel.isChanging = false;
vm.page.saveButtonState = "success";
}, function (err) {
diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.html b/src/Umbraco.Web.UI.Client/src/views/users/user.html
index 517dde209d..4c9c377737 100644
--- a/src/Umbraco.Web.UI.Client/src/views/users/user.html
+++ b/src/Umbraco.Web.UI.Client/src/views/users/user.html
@@ -24,7 +24,8 @@
+
+
+
Password
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Permissions
diff --git a/src/Umbraco.Web/Editors/CurrentUserController.cs b/src/Umbraco.Web/Editors/CurrentUserController.cs
index d7da1cd713..1781a00b3a 100644
--- a/src/Umbraco.Web/Editors/CurrentUserController.cs
+++ b/src/Umbraco.Web/Editors/CurrentUserController.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
@@ -8,6 +6,7 @@ using System.Web.Http;
using System.Web.Security;
using AutoMapper;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Services;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Mapping;
@@ -39,29 +38,16 @@ namespace Umbraco.Web.Editors
///
public ModelWithNotifications
PostChangePassword(ChangingPasswordModel data)
{
- var userProvider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider();
-
- //TODO: WE need to support this! - requires UI updates, etc...
- if (userProvider.RequiresQuestionAndAnswer)
- {
- throw new NotSupportedException("Currently the user editor does not support providers that have RequiresQuestionAndAnswer specified");
- }
-
- var passwordChangeResult = Members.ChangePassword(Security.CurrentUser.Username, data, userProvider);
+ var passwordChangeResult = PasswordChangeControllerHelper.PostChangePassword(Security.CurrentUser, data, ModelState, Members);
+
if (passwordChangeResult.Success)
{
//even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword
var result = new ModelWithNotifications(passwordChangeResult.Result.ResetPassword);
- result.AddSuccessNotification(ui.Text("user", "password"), ui.Text("user", "passwordChanged"));
+ result.AddSuccessNotification(Services.TextService.Localize("user/password"), Services.TextService.Localize("user/passwordChanged"));
return result;
}
- //it wasn't successful, so add the change error to the model state, we've name the property alias _umb_password on the form
- // so that is why it is being used here.
- ModelState.AddPropertyError(
- passwordChangeResult.Result.ChangeError,
- string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
-
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
diff --git a/src/Umbraco.Web/Editors/PasswordChangeControllerHelper.cs b/src/Umbraco.Web/Editors/PasswordChangeControllerHelper.cs
new file mode 100644
index 0000000000..d9602d4ecd
--- /dev/null
+++ b/src/Umbraco.Web/Editors/PasswordChangeControllerHelper.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Linq;
+using System.Web.Http.ModelBinding;
+using Umbraco.Core;
+using Umbraco.Core.Models.Membership;
+using Umbraco.Web.Models;
+using Umbraco.Web.Security;
+
+namespace Umbraco.Web.Editors
+{
+ internal class PasswordChangeControllerHelper
+ {
+
+ public static Attempt PostChangePassword(
+ IUser currentUser,
+ ChangingPasswordModel data,
+ ModelStateDictionary modelState,
+ MembershipHelper membersHelper)
+ {
+ var userProvider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider();
+
+ if (userProvider.RequiresQuestionAndAnswer)
+ {
+ throw new NotSupportedException("Currently the user editor does not support providers that have RequiresQuestionAndAnswer specified");
+ }
+
+ var passwordChangeResult = membersHelper.ChangePassword(currentUser.Username, data, userProvider);
+ if (passwordChangeResult.Success == false)
+ {
+ //it wasn't successful, so add the change error to the model state
+ var fieldName = passwordChangeResult.Result.ChangeError.MemberNames.FirstOrDefault() ?? "password";
+ modelState.AddModelError(fieldName,
+ passwordChangeResult.Result.ChangeError.ErrorMessage);
+ }
+
+ return passwordChangeResult;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs
index b5ed991722..6226400fd0 100644
--- a/src/Umbraco.Web/Editors/UsersController.cs
+++ b/src/Umbraco.Web/Editors/UsersController.cs
@@ -33,8 +33,6 @@ using Task = System.Threading.Tasks.Task;
namespace Umbraco.Web.Editors
{
-
-
[PluginController("UmbracoApi")]
[UmbracoApplicationAuthorize(Constants.Applications.Users)]
public class UsersController : UmbracoAuthorizedJsonController
@@ -438,11 +436,24 @@ namespace Umbraco.Web.Editors
hasErrors = true;
}
+ var resetPasswordValue = string.Empty;
+ if (userSave.ChangePassword != null)
+ {
+ var passwordChangeResult = PasswordChangeControllerHelper.PostChangePassword(Security.CurrentUser, userSave.ChangePassword, ModelState, Members);
+ if (passwordChangeResult.Success)
+ {
+ //depending on how the provider is configured, the password may be reset so let's store that for later
+ resetPasswordValue = passwordChangeResult.Result.ResetPassword;
+ }
+ else
+ {
+ hasErrors = true;
+ }
+ }
+
if (hasErrors)
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
- //TODO: More validation, password changing logic, persisting
-
//merge the save data onto the user
var user = Mapper.Map(userSave, found);
@@ -450,6 +461,10 @@ namespace Umbraco.Web.Editors
var display = Mapper.Map(user);
+ //re-map the password reset value (if any)
+ if (resetPasswordValue.IsNullOrWhiteSpace() == false)
+ display.ResetPasswordValue = resetPasswordValue;
+
display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/operationSavedHeader"), Services.TextService.Localize("speechBubbles/editUserSaved"));
return display;
}
diff --git a/src/Umbraco.Web/ModelStateExtensions.cs b/src/Umbraco.Web/ModelStateExtensions.cs
index 5df479ce76..a36f3c89f6 100644
--- a/src/Umbraco.Web/ModelStateExtensions.cs
+++ b/src/Umbraco.Web/ModelStateExtensions.cs
@@ -61,7 +61,7 @@ namespace Umbraco.Web
{
//if there are no member names supplied then we assume that the validation message is for the overall property
// not a sub field on the property editor
- if (!result.MemberNames.Any())
+ if (result.MemberNames.Any() == false)
{
//add a model state error for the entire property
modelState.AddModelError(string.Format("{0}.{1}", "_Properties", propertyAlias), result.ErrorMessage);
diff --git a/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs
index 1c3b19ab90..b747c7f348 100644
--- a/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs
@@ -35,10 +35,10 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "startMediaIds")]
public IEnumerable StartMediaIds { get; set; }
- /////
- ///// A list of sections the user is allowed to view based on their current groups assigned
- /////
- //[DataMember(Name = "allowedSections")]
- //public IEnumerable AllowedSections { get; set; }
+ ///
+ /// If the password is reset on save, this value will be populated
+ ///
+ [DataMember(Name = "resetPasswordValue")]
+ public string ResetPasswordValue { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/ContentEditing/UserSave.cs b/src/Umbraco.Web/Models/ContentEditing/UserSave.cs
index 4b408d324e..30f6bfc420 100644
--- a/src/Umbraco.Web/Models/ContentEditing/UserSave.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/UserSave.cs
@@ -15,7 +15,8 @@ namespace Umbraco.Web.Models.ContentEditing
[DataContract(Name = "user", Namespace = "")]
public class UserSave : EntityBasic, IValidatableObject
{
- //TODO: There will be more information to save along with the structure for changing passwords
+ [DataMember(Name = "changePassword", IsRequired = true)]
+ public ChangingPasswordModel ChangePassword { get; set; }
[DataMember(Name = "id", IsRequired = true)]
[Required]
diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs
index 605b5137d8..81487c41d9 100644
--- a/src/Umbraco.Web/Security/MembershipHelper.cs
+++ b/src/Umbraco.Web/Security/MembershipHelper.cs
@@ -719,7 +719,7 @@ namespace Umbraco.Web.Security
if (passwordModel.OldPassword.IsNullOrWhiteSpace() && membershipProvider.EnablePasswordRetrieval == false)
{
//if password retrieval is not enabled but there is no old password we cannot continue
- return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password cannot be changed without the old password", new[] { "value" }) });
+ return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password cannot be changed without the old password", new[] { "oldPassword" }) });
}
if (passwordModel.OldPassword.IsNullOrWhiteSpace() == false)
@@ -730,7 +730,7 @@ namespace Umbraco.Web.Security
{
var result = membershipProvider.ChangePassword(username, passwordModel.OldPassword, passwordModel.NewPassword);
return result == false
- ? Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid username or password", new[] { "value" }) })
+ ? Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid username or password", new[] { "oldPassword" }) })
: Attempt.Succeed(new PasswordChangedModel());
}
catch (Exception ex)
@@ -743,7 +743,7 @@ namespace Umbraco.Web.Security
if (membershipProvider.EnablePasswordRetrieval == false)
{
//we cannot continue if we cannot get the current password
- return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password cannot be changed without the old password", new[] { "value" }) });
+ return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password cannot be changed without the old password", new[] { "oldPassword" }) });
}
if (membershipProvider.RequiresQuestionAndAnswer && passwordModel.Answer.IsNullOrWhiteSpace())
{
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 587e26f0fd..58df77c82d 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -331,6 +331,7 @@
+