diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index f90e847289..011726e615 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -464,7 +464,7 @@ namespace Umbraco.Core.Models /// also reset the dirty changes made to the content's Properties (user defined) /// /// - internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) + public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) { base.ResetDirtyProperties(rememberPreviouslyChangedProperties); diff --git a/src/Umbraco.Core/Models/EntityBase/IRememberBeingDirty.cs b/src/Umbraco.Core/Models/EntityBase/IRememberBeingDirty.cs index 94a88d312e..efebbd7c74 100644 --- a/src/Umbraco.Core/Models/EntityBase/IRememberBeingDirty.cs +++ b/src/Umbraco.Core/Models/EntityBase/IRememberBeingDirty.cs @@ -9,5 +9,6 @@ bool WasDirty(); bool WasPropertyDirty(string propertyName); void ForgetPreviouslyDirtyProperties(); + void ResetDirtyProperties(bool rememberPreviouslyChangedProperties); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/EntityBase/TracksChangesEntityBase.cs b/src/Umbraco.Core/Models/EntityBase/TracksChangesEntityBase.cs index dc65c583bc..cf34296281 100644 --- a/src/Umbraco.Core/Models/EntityBase/TracksChangesEntityBase.cs +++ b/src/Umbraco.Core/Models/EntityBase/TracksChangesEntityBase.cs @@ -108,7 +108,7 @@ namespace Umbraco.Core.Models.EntityBase /// Please note that resetting the dirty properties could potentially /// obstruct the saving of a new or updated entity. /// - internal virtual void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) + public virtual void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) { if (rememberPreviouslyChangedProperties) { diff --git a/src/Umbraco.Core/Models/Macro.cs b/src/Umbraco.Core/Models/Macro.cs index c754cab326..a0f0bd9651 100644 --- a/src/Umbraco.Core/Models/Macro.cs +++ b/src/Umbraco.Core/Models/Macro.cs @@ -172,7 +172,7 @@ namespace Umbraco.Core.Models OnPropertyChanged(PropertiesSelector); } - internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) + public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) { _addedProperties.Clear(); _removedProperties.Clear(); diff --git a/src/Umbraco.Core/Models/Membership/UserProfile.cs b/src/Umbraco.Core/Models/Membership/UserProfile.cs index 1e3ab541e7..61a0d68ba7 100644 --- a/src/Umbraco.Core/Models/Membership/UserProfile.cs +++ b/src/Umbraco.Core/Models/Membership/UserProfile.cs @@ -133,7 +133,7 @@ namespace Umbraco.Core.Models.Membership /// be flagged as dirty. /// /// - internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) + public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) { _addedSections.Clear(); _removedSections.Clear(); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index a66c3b549e..1fc040527a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -221,12 +221,7 @@ function umbDataFormatter() { }); saveModel.email = propEmail.value; saveModel.username = propLogin.value; - //NOTE: This would only be set for new members! - if (angular.isString(propPass.value)) { - // if we are resetting or changing passwords then that data will come from the property editor and - // it's value will be an object not just a string. - saveModel.password = propPass.value; - } + saveModel.password = propPass.value; return saveModel; }, diff --git a/src/Umbraco.Web.UI.Client/src/views/member/edit.html b/src/Umbraco.Web.UI.Client/src/views/member/edit.html index 3fccfda95d..5501ccb381 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/edit.html @@ -36,6 +36,7 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.controller.js index 9f60d300cd..e5d8739d51 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.controller.js @@ -6,29 +6,44 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.ChangePasswordCont //based on the membership provider cannot always be ported to js from .net directly. /* { + hasPassword: true/false, requiresQuestionAnswer: true/false, enableReset: true/false, + enablePasswordRetrieval: true/false, minPasswordLength: 10 } */ //set defaults if they are not available - if (!$scope.model.config || !$scope.model.config.requiresQuestionAnswer) { + if (!$scope.model.config || $scope.model.config.hasPassword === undefined) { + $scope.model.config.hasPassword = false; + } + if (!$scope.model.config || $scope.model.config.enablePasswordRetrieval === undefined) { + $scope.model.config.enablePasswordRetrieval = true; + } + if (!$scope.model.config || $scope.model.config.requiresQuestionAnswer === undefined) { $scope.model.config.requiresQuestionAnswer = false; } - if (!$scope.model.config || !$scope.model.config.enableReset) { + if (!$scope.model.config || $scope.model.config.enableReset === undefined) { $scope.model.config.enableReset = true; } - if (!$scope.model.config || !$scope.model.config.minPasswordLength) { - $scope.model.config.minPasswordLength = 7; + if (!$scope.model.config || $scope.model.config.minPasswordLength === undefined) { + $scope.model.config.minPasswordLength = 0; } - + //set the model defaults - we never get supplied a password from the server so this is ok to overwrite. + $scope.model.value = { + newPassword: "", + oldPassword: null, + reset: null, + answer: null + }; + //the value to compare to match passwords $scope.confirm = ""; - $scope.hasPassword = $scope.model.value !== undefined && $scope.model.value !== null && $scope.model.value !== ""; - - $scope.changing = !$scope.hasPassword; + //if there is no password saved for this entity , it must be new so we do not allow toggling of the change password, it is always there + //with validators turned on. + $scope.changing = !$scope.model.config.hasPassword; $scope.doChange = function() { $scope.changing = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.html index c624a81cc2..fa67b50b53 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/changepassword/changepassword.html @@ -1,33 +1,58 @@ 
- -
- Password changing or resetting is currently not supported -
+ Change password
-
- - - - Required - Minimum {{model.config.minPasswordLength}} characters - - + +
+ +
+ + +
+
+ + +
+ +
+ + Required + +
-
- - - Passwords must match +
+ +
+ + Required + Minimum {{model.config.minPasswordLength}} characters + +
+
+ +
+ +
+ + Passwords must match +
- + + Cancel
\ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 9784c9e309..3b98328238 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2167,7 +2167,6 @@ - Web.Template.config Designer diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index a17817ced4..8ad5f68422 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -11,6 +11,8 @@ using System.Web.Security; using AutoMapper; using Examine.LuceneEngine.SearchCriteria; using Examine.SearchCriteria; +using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Web.WebApi; @@ -21,7 +23,6 @@ using Umbraco.Web.WebApi.Filters; using umbraco; using Constants = Umbraco.Core.Constants; using Examine; -using Member = umbraco.cms.businesslogic.member.Member; namespace Umbraco.Web.Editors { @@ -38,7 +39,7 @@ namespace Umbraco.Web.Editors /// public MemberController() : this(UmbracoContext.Current) - { + { } /// @@ -57,7 +58,7 @@ namespace Umbraco.Web.Editors /// public MemberDisplay GetByKey(Guid key) { - if (Member.InUmbracoMemberMode()) + if (Membership.Provider.Name == Constants.Conventions.Member.UmbracoMemberProviderName) { var foundMember = Services.MemberService.GetByKey(key); if (foundMember == null) @@ -71,7 +72,7 @@ namespace Umbraco.Web.Editors //TODO: Support this throw new HttpResponseException(Request.CreateValidationErrorResponse("Editing member with a non-umbraco membership provider is currently not supported")); } - + } /// @@ -118,7 +119,7 @@ namespace Umbraco.Web.Editors //Unlike content/media - if there are errors for a member, we do NOT proceed to save them, we cannot so return the errors if (ModelState.IsValid == false) - { + { var forDisplay = Mapper.Map(contentItem.PersistedContent); forDisplay.Errors = ModelState.ToErrorDictionary(); throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay)); @@ -135,7 +136,7 @@ namespace Umbraco.Web.Editors switch (contentItem.Action) { case ContentSaveAction.Save: - //TODO: Update with the provider! - change password, etc... + UpdateWithMembershipProvider(contentItem); break; case ContentSaveAction.SaveNew: MembershipCreateStatus status; @@ -144,7 +145,7 @@ namespace Umbraco.Web.Editors default: //we don't support anything else for members throw new HttpResponseException(HttpStatusCode.NotFound); - } + } //If we've had problems creating/updating the user with the provider then return the error if (ModelState.IsValid == false) @@ -153,7 +154,7 @@ namespace Umbraco.Web.Editors forDisplay.Errors = ModelState.ToErrorDictionary(); throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay)); } - + //save the item //NOTE: We are setting the password to NULL - this indicates to the system to not actually save the password // so it will not get overwritten! @@ -196,6 +197,177 @@ namespace Umbraco.Web.Editors base.MapPropertyValues(contentItem); } + /// + /// Update the membership user using the membership provider (for things like email, etc...) + /// If a password change is detected then we'll try that too. + /// + /// + /// + /// + /// YES! It is completely insane how many options you have to take into account based on the membership provider. yikes! + /// + /// TODO: We need to update this method to return the new password if it has been reset and then show that to the UI! + /// + /// + private void UpdateWithMembershipProvider(MemberSave contentItem) + { + //Get the member from the provider + var membershipUser = Membership.Provider.GetUser(contentItem.PersistedContent.Username, false); + if (membershipUser == null) + { + //This should never happen! so we'll let it YSOD if it does. + throw new InvalidOperationException("Could not get member from membership provider " + Membership.Provider.Name + " with username " + contentItem.PersistedContent.Username); + } + + //ok, first thing to do is check if they've changed their email + //TODO: When we support the other membership provider data then we'll check if any of that's changed too. + if (contentItem.Email.Trim().InvariantEquals(membershipUser.Email) == false) + { + membershipUser.Email = contentItem.Email.Trim(); + + try + { + Membership.Provider.UpdateUser(membershipUser); + } + catch (Exception ex) + { + LogHelper.WarnWithException("Could not update member, the provider returned an error", ex); + ModelState.AddPropertyError( + //specify 'default' just so that it shows up as a notification - is not assigned to a property + new ValidationResult("Could not update member, the provider returned an error: " + ex.Message + " (see log for full details)"), "default"); + } + } + + //password changes ? + if (contentItem.Password == null) return; + + //Are we resetting the password?? + if (contentItem.Password.Reset.HasValue && contentItem.Password.Reset.Value) + { + if (Membership.Provider.EnablePasswordReset == false) + { + ModelState.AddPropertyError( + new ValidationResult("Password reset is not enabled", new[] { "resetPassword" }), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + else if (Membership.Provider.RequiresQuestionAndAnswer && contentItem.Password.Answer.IsNullOrWhiteSpace()) + { + ModelState.AddPropertyError( + new ValidationResult("Password reset requires a password answer", new[] {"resetPassword"}), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + else + { + //ok, we should be able to reset it + try + { + var newPass = Membership.Provider.ResetPassword( + membershipUser.UserName, + Membership.Provider.RequiresQuestionAndAnswer ? contentItem.Password.Answer : null); + + //TODO: How do we show this new password to the front-end ??? + } + catch (Exception ex) + { + LogHelper.WarnWithException("Could not reset member password", ex); + ModelState.AddPropertyError( + new ValidationResult("Could not reset password, error: " + ex.Message + " (see log for full details)", new[] {"resetPassword"}), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + } + } + else if (contentItem.Password.NewPassword.IsNullOrWhiteSpace() == false) + { + //we're not resetting it so we need to try to change it. + + if (contentItem.Password.OldPassword.IsNullOrWhiteSpace() && Membership.Provider.EnablePasswordRetrieval == false) + { + //if password retrieval is not enabled but there is no old password we cannot continue + + ModelState.AddPropertyError( + new ValidationResult("Password cannot be changed without the old password", new[] {"value"}), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + else if (contentItem.Password.OldPassword.IsNullOrWhiteSpace() == false) + { + //if an old password is suplied try to change it + + try + { + var result = Membership.Provider.ChangePassword(membershipUser.UserName, contentItem.Password.OldPassword, contentItem.Password.NewPassword); + if (result == false) + { + ModelState.AddPropertyError( + new ValidationResult("Could not change password", new[] {"value"}), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + } + catch (Exception ex) + { + LogHelper.WarnWithException("Could not change member password", ex); + ModelState.AddPropertyError( + new ValidationResult("Could not change password, error: " + ex.Message + " (see log for full details)", new[] {"value"}), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + } + else if (Membership.Provider.EnablePasswordRetrieval == false) + { + //we cannot continue if we cannot get the current password + + ModelState.AddPropertyError( + new ValidationResult("Password cannot be changed without the old password", new[] {"value"}), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + else if (Membership.Provider.RequiresQuestionAndAnswer && contentItem.Password.Answer.IsNullOrWhiteSpace()) + { + //if the question answer is required but there isn't one, we cannot continue + + ModelState.AddPropertyError( + new ValidationResult("Password cannot be changed without the password answer", new[] {"value"}), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + + } + else + { + //lets try to get the old one so we can change it + + try + { + var oldPassword = Membership.Provider.GetPassword( + membershipUser.UserName, + Membership.Provider.RequiresQuestionAndAnswer ? contentItem.Password.Answer : null); + + try + { + var result = Membership.Provider.ChangePassword(membershipUser.UserName, oldPassword, contentItem.Password.NewPassword); + if (result == false) + { + ModelState.AddPropertyError( + new ValidationResult("Could not change password", new[] {"value"}), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + } + catch (Exception ex1) + { + LogHelper.WarnWithException("Could not change member password", ex1); + ModelState.AddPropertyError( + new ValidationResult("Could not change password, error: " + ex1.Message + " (see log for full details)", new[] { "value" }), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + + } + catch (Exception ex2) + { + LogHelper.WarnWithException("Could not retrieve member password", ex2); + ModelState.AddPropertyError( + new ValidationResult("Could not change password, error: " + ex2.Message + " (see log for full details)", new[] { "value" }), + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + } + + } + } + } + /// /// This is going to create the user with the membership provider and check for validation /// @@ -212,18 +384,20 @@ namespace Umbraco.Web.Editors //TODO: I think we should detect if the Umbraco membership provider is active, if so then we'll create the member first and the provider key doesn't matter // but if we are using a 3rd party membership provider - then we should create our IMember first and use it's key as their provider user key! - + //NOTE: We are casting directly to the umbraco membership provider so we can specify the member type that we want to use! - + var umbracoMembershipProvider = (global::umbraco.providers.members.UmbracoMembershipProvider)Membership.Provider; var membershipUser = umbracoMembershipProvider.CreateUser( - contentItem.ContentTypeAlias, contentItem.Username, contentItem.Password, contentItem.Email, "", "", true, Guid.NewGuid(), out status); + contentItem.ContentTypeAlias, contentItem.Username, + contentItem.Password.NewPassword, + contentItem.Email, "", "", true, Guid.NewGuid(), out status); //TODO: Localize these! switch (status) { case MembershipCreateStatus.Success: - + //Go and re-fetch the persisted item contentItem.PersistedContent = Services.MemberService.GetByUsername(contentItem.Username.Trim()); //remap the values to save diff --git a/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs b/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs index 8fbd226f0f..71943baea2 100644 --- a/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs +++ b/src/Umbraco.Web/Editors/MembershipProviderValidationFilterAttribute.cs @@ -57,7 +57,7 @@ namespace Umbraco.Web.Editors { //they are changing their login name if (existingByEmail.Cast().Select(x => x.UserName) - .Any(x => x.InvariantEquals(contentItem.Username.Trim()))) + .Any(x => x == contentItem.Username.Trim())) { //the user cannot use this login return false; @@ -67,7 +67,7 @@ namespace Umbraco.Web.Editors case ContentSaveAction.SaveNew: //check if the user's login already exists if (existingByEmail.Cast().Select(x => x.UserName) - .Any(x => x.InvariantEquals(contentItem.Username.Trim()))) + .Any(x => x == contentItem.Username.Trim())) { //the user cannot use this login return false; diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberPassword.cs b/src/Umbraco.Web/Models/ContentEditing/MemberPassword.cs new file mode 100644 index 0000000000..3a1c67842c --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/MemberPassword.cs @@ -0,0 +1,37 @@ +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + /// + /// A model representing the data required to set a member/user password depending on the provider installed. + /// + public class MemberPassword + { + /// + /// The password value + /// + /// + /// This + /// + [DataMember(Name = "newPassword")] + public string NewPassword { get; set; } + + /// + /// The old password - used to change a password when: EnablePasswordRetrieval = false + /// + [DataMember(Name = "oldPassword")] + public string OldPassword { get; set; } + + /// + /// Set to true if the password is to be reset - only valid when: EnablePasswordReset = true + /// + [DataMember(Name = "reset")] + public bool? Reset { get; set; } + + /// + /// The password answer - required for reset when: RequiresQuestionAndAnswer = true + /// + [DataMember(Name = "answer")] + public string Answer { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberSave.cs b/src/Umbraco.Web/Models/ContentEditing/MemberSave.cs index 8de5f3647c..ace4d576f9 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MemberSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/MemberSave.cs @@ -1,4 +1,5 @@ using System.Runtime.Serialization; +using Newtonsoft.Json.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.Validation; @@ -9,6 +10,11 @@ namespace Umbraco.Web.Models.ContentEditing /// public class MemberSave : ContentBaseItemSave { + public MemberSave() + { + Password = new MemberPassword(); + } + [DataMember(Name = "username", IsRequired = true)] [RequiredForPersistence(AllowEmptyStrings = false, ErrorMessage = "Required")] public string Username { get; set; } @@ -16,8 +22,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "email", IsRequired = true)] [RequiredForPersistence(AllowEmptyStrings = false, ErrorMessage = "Required")] public string Email { get; set; } - - [DataMember(Name = "password")] - public string Password { get; set; } + + [DataMember(Name = "password")] + public MemberPassword Password { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs index 37690321c9..da490d6f31 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Web.Security; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; @@ -66,6 +67,8 @@ namespace Umbraco.Web.Models.Mapping /// private static void MapGenericCustomProperties(IMember member, MemberDisplay display) { + var membershipProvider = Membership.Provider; + TabsAndPropertiesResolver.MapGenericProperties( member, display, new ContentPropertyDisplay @@ -81,7 +84,17 @@ namespace Umbraco.Web.Models.Mapping Alias = string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix), Label = ui.Text("password"), Value = "", - View = "changepassword" //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor + View = "changepassword",//TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor + Config = new Dictionary + { + //the password change toggle will only be displayed if there is already a password assigned. + { "hasPassword", member.Password.IsNullOrWhiteSpace() == false }, + { "minPasswordLength", membershipProvider.MinRequiredPasswordLength }, + { "enableReset", membershipProvider.EnablePasswordReset }, + { "enablePasswordRetrieval" , membershipProvider.EnablePasswordRetrieval }, + { "requiresQuestionAnswer", membershipProvider.RequiresQuestionAndAnswer } + //TODO: Inject the other parameters in here to change the behavior of this control - based on the membership provider settings. + } }, new ContentPropertyDisplay { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index dbf529f4ce..e1747a481f 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -311,6 +311,7 @@ + diff --git a/src/Umbraco.Web/WebApi/Binders/MemberBinder.cs b/src/Umbraco.Web/WebApi/Binders/MemberBinder.cs index aabfdcb7b9..44499fd6b2 100644 --- a/src/Umbraco.Web/WebApi/Binders/MemberBinder.cs +++ b/src/Umbraco.Web/WebApi/Binders/MemberBinder.cs @@ -54,7 +54,7 @@ namespace Umbraco.Web.WebApi.Binders } //return the new member with the details filled in - return new Member(model.Name, model.Email, model.Username, model.Password, -1, contentType); + return new Member(model.Name, model.Email, model.Username, model.Password.NewPassword, -1, contentType); } protected override ContentItemDto MapFromPersisted(MemberSave model)