diff --git a/src/Umbraco.Core/Security/BackOfficeUserManager.cs b/src/Umbraco.Core/Security/BackOfficeUserManager.cs index ad22c1426e..993f0671f3 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserManager.cs @@ -137,7 +137,9 @@ namespace Umbraco.Core.Security /// Initializes the user manager with the correct options /// /// - /// + /// + /// The for the users called UsersMembershipProvider + /// /// /// protected void InitUserManager( @@ -153,11 +155,10 @@ namespace Umbraco.Core.Security }; // Configure validation logic for passwords - var provider = MembershipProviderExtensions.GetUsersMembershipProvider(); - manager.PasswordValidator = new MembershipProviderPasswordValidator(provider); + manager.PasswordValidator = new MembershipProviderPasswordValidator(membershipProvider); //use a custom hasher based on our membership provider - manager.PasswordHasher = new MembershipPasswordHasher(membershipProvider); + manager.PasswordHasher = new MembershipProviderPasswordHasher(membershipProvider); if (dataProtectionProvider != null) { diff --git a/src/Umbraco.Core/Security/MembershipPasswordHasher.cs b/src/Umbraco.Core/Security/MembershipPasswordHasher.cs deleted file mode 100644 index 56daa3efdd..0000000000 --- a/src/Umbraco.Core/Security/MembershipPasswordHasher.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.AspNet.Identity; - -namespace Umbraco.Core.Security -{ - /// - /// A custom password hasher that conforms to the current password hashing done in Umbraco - /// - internal class MembershipPasswordHasher : IPasswordHasher - { - private readonly MembershipProviderBase _provider; - - public MembershipPasswordHasher(MembershipProviderBase provider) - { - _provider = provider; - } - - public string HashPassword(string password) - { - return _provider.HashPasswordForStorage(password); - } - - public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword) - { - return _provider.VerifyPassword(providedPassword, hashedPassword) - ? PasswordVerificationResult.Success - : PasswordVerificationResult.Failed; - } - - - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Security/MembershipProviderPasswordHasher.cs b/src/Umbraco.Core/Security/MembershipProviderPasswordHasher.cs new file mode 100644 index 0000000000..01f62ca51b --- /dev/null +++ b/src/Umbraco.Core/Security/MembershipProviderPasswordHasher.cs @@ -0,0 +1,47 @@ +using System; +using Microsoft.AspNet.Identity; + +namespace Umbraco.Core.Security +{ + public interface IUserAwarePasswordHasher + where TKey : IEquatable + { + string HashPassword(TKey id, string password); + string VerifyHashedPassword(TKey id, string hashedPassword, string providedPassword); + } + + public interface IMembershipProviderPasswordHasher : IPasswordHasher + { + MembershipProviderBase MembershipProvider { get; } + } + + /// + /// A custom password hasher that conforms to the password hashing done with membership providers + /// + public class MembershipProviderPasswordHasher : IMembershipProviderPasswordHasher + { + /// + /// Exposes the underlying MembershipProvider + /// + public MembershipProviderBase MembershipProvider { get; private set; } + + public MembershipProviderPasswordHasher(MembershipProviderBase provider) + { + MembershipProvider = provider; + } + + public string HashPassword(string password) + { + return MembershipProvider.HashPasswordForStorage(password); + } + + public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword) + { + return MembershipProvider.VerifyPassword(providedPassword, hashedPassword) + ? PasswordVerificationResult.Success + : PasswordVerificationResult.Failed; + } + + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 0edbdc77cf..4d46e6c319 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -663,7 +663,7 @@ - + diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index f61e11a791..f38a9965cd 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -56,7 +56,9 @@ namespace Umbraco.Web.Editors /// [WebApi.UmbracoAuthorize(requireApproval: false)] public IDictionary GetMembershipProviderConfig() - { + { + //TODO: Check if the current PasswordValidator is an IMembershipProviderPasswordValidator, if + //it's not than we should return some generic defaults var provider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider(); return provider.GetConfiguration(Services.UserService); } diff --git a/src/Umbraco.Web/Editors/CurrentUserController.cs b/src/Umbraco.Web/Editors/CurrentUserController.cs index 8db46ec1e8..a374c5579d 100644 --- a/src/Umbraco.Web/Editors/CurrentUserController.cs +++ b/src/Umbraco.Web/Editors/CurrentUserController.cs @@ -86,9 +86,10 @@ namespace Umbraco.Web.Editors /// /// If the password is being reset it will return the newly reset password, otherwise will return an empty value /// - public ModelWithNotifications PostChangePassword(ChangingPasswordModel data) + public async Task> PostChangePassword(ChangingPasswordModel data) { - var passwordChangeResult = PasswordChangeControllerHelper.ChangePassword(Security.CurrentUser, data, ModelState, Members); + var passwordChanger = new PasswordChanger(Logger, Services.UserService); + var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, data, ModelState, UserManager); if (passwordChangeResult.Success) { diff --git a/src/Umbraco.Web/Editors/PasswordChangeControllerHelper.cs b/src/Umbraco.Web/Editors/PasswordChangeControllerHelper.cs deleted file mode 100644 index 899e0eb9da..0000000000 --- a/src/Umbraco.Web/Editors/PasswordChangeControllerHelper.cs +++ /dev/null @@ -1,39 +0,0 @@ -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 ChangePassword( - 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/PasswordChanger.cs b/src/Umbraco.Web/Editors/PasswordChanger.cs new file mode 100644 index 0000000000..ffd5a9b0fd --- /dev/null +++ b/src/Umbraco.Web/Editors/PasswordChanger.cs @@ -0,0 +1,246 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using System.Web.Http.ModelBinding; +using System.Web.Security; +using Microsoft.AspNet.Identity; +using umbraco.cms.businesslogic.packager; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.Identity; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Web.Models; +using Umbraco.Web.Security; +using IUser = Umbraco.Core.Models.Membership.IUser; + +namespace Umbraco.Web.Editors +{ + internal class PasswordChanger + { + private readonly ILogger _logger; + private readonly IUserService _userService; + + public PasswordChanger(ILogger logger, IUserService userService) + { + _logger = logger; + _userService = userService; + } + + public async Task> ChangePasswordWithIdentityAsync( + IUser currentUser, + ChangingPasswordModel passwordModel, + ModelStateDictionary modelState, + BackOfficeUserManager userMgr) + { + if (passwordModel == null) throw new ArgumentNullException("passwordModel"); + if (userMgr == null) throw new ArgumentNullException("userMgr"); + + //check if this identity implementation is powered by an underlying membership provider (it will be in most cases) + var membershipPasswordHasher = userMgr.PasswordHasher as IMembershipProviderPasswordHasher; + + //check if this identity implementation is powered by an IUserAwarePasswordHasher (it will be by default in 7.7+ but not for upgrades) + var userAwarePasswordHasher = userMgr.PasswordHasher as IUserAwarePasswordHasher; + + if (membershipPasswordHasher != null && userAwarePasswordHasher == null) + { + //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); + } + + //get the real password validator, thsi should not be null but in some very rare cases it could be, in which case + //we need to create a default password validator to use since we have no idea what it actually is or what it's rules are + //this is an Edge Case! + var passwordValidator = userMgr.PasswordValidator as PasswordValidator + ?? (membershipPasswordHasher != null + ? new MembershipProviderPasswordValidator(membershipPasswordHasher.MembershipProvider) + : new PasswordValidator()); + + //Are we resetting the password?? + if (passwordModel.Reset.HasValue && passwordModel.Reset.Value) + { + //ok, we should be able to reset it + var resetToken = await userMgr.GeneratePasswordResetTokenAsync(currentUser.Id); + var newPass = Membership.GeneratePassword(passwordValidator.RequiredLength, passwordValidator.RequireNonLetterOrDigit ? 2 : 0); + var resetResult = await userMgr.ResetPasswordAsync(currentUser.Id, resetToken, newPass); + + if (resetResult.Succeeded == false) + { + var errors = string.Join(". ", resetResult.Errors); + _logger.Warn(string.Format("Could not reset member password {0}", errors)); + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not reset password, errors: " + errors, new[] { "resetPassword" }) }); + } + + return Attempt.Succeed(new PasswordChangedModel { ResetPassword = newPass }); + } + + //we're not resetting it so we need to try to change it. + + if (passwordModel.NewPassword.IsNullOrWhiteSpace()) + { + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Cannot set an empty password", new[] { "value" }) }); + } + + //we cannot arbitrarily change the password without knowing the old one and no old password was supplied - need to return an error + //TODO: What if the current user is admin? We should allow manually changing then? + if (passwordModel.OldPassword.IsNullOrWhiteSpace()) + { + //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[] { "oldPassword" }) }); + } + + 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); + if (changeResult.Succeeded == false) + { + var errors = string.Join(". ", changeResult.Errors); + _logger.Warn(string.Format("Could not change member password {0}", errors)); + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, errors: " + errors, new[] { "value" }) }); + } + return Attempt.Succeed(new PasswordChangedModel()); + } + + //We shouldn't really get here + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid information supplied", new[] { "value" }) }); + } + + /// + /// Changes password for a member/user given the membership provider and the password change model + /// + /// + /// + /// + /// + public Attempt ChangePasswordWithMembershipProvider(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider) + { + // YES! It is completely insane how many options you have to take into account based on the membership provider. yikes! + + if (passwordModel == null) throw new ArgumentNullException("passwordModel"); + if (membershipProvider == null) throw new ArgumentNullException("membershipProvider"); + + //Are we resetting the password?? + if (passwordModel.Reset.HasValue && passwordModel.Reset.Value) + { + var canReset = membershipProvider.CanResetPassword(_userService); + if (canReset == false) + { + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password reset is not enabled", new[] { "resetPassword" }) }); + } + if (membershipProvider.RequiresQuestionAndAnswer && passwordModel.Answer.IsNullOrWhiteSpace()) + { + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password reset requires a password answer", new[] { "resetPassword" }) }); + } + //ok, we should be able to reset it + try + { + var newPass = membershipProvider.ResetPassword( + username, + membershipProvider.RequiresQuestionAndAnswer ? passwordModel.Answer : null); + + //return the generated pword + return Attempt.Succeed(new PasswordChangedModel { ResetPassword = newPass }); + } + catch (Exception ex) + { + _logger.WarnWithException("Could not reset member password", ex); + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not reset password, error: " + ex.Message + " (see log for full details)", new[] { "resetPassword" }) }); + } + } + + //we're not resetting it so we need to try to change it. + + if (passwordModel.NewPassword.IsNullOrWhiteSpace()) + { + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Cannot set an empty password", new[] { "value" }) }); + } + + //This is an edge case and is only necessary for backwards compatibility: + var umbracoBaseProvider = membershipProvider as MembershipProviderBase; + if (umbracoBaseProvider != null && umbracoBaseProvider.AllowManuallyChangingPassword) + { + //this provider allows manually changing the password without the old password, so we can just do it + try + { + var result = umbracoBaseProvider.ChangePassword(username, "", passwordModel.NewPassword); + return result == false + ? Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid username or password", new[] { "value" }) }) + : Attempt.Succeed(new PasswordChangedModel()); + } + catch (Exception ex) + { + _logger.WarnWithException("Could not change member password", ex); + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex.Message + " (see log for full details)", new[] { "value" }) }); + } + } + + //The provider does not support manually chaning the password but no old password supplied - need to return an error + 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[] { "oldPassword" }) }); + } + + if (passwordModel.OldPassword.IsNullOrWhiteSpace() == false) + { + //if an old password is suplied try to change it + + try + { + 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[] { "oldPassword" }) }) + : Attempt.Succeed(new PasswordChangedModel()); + } + catch (Exception ex) + { + _logger.WarnWithException("Could not change member password", ex); + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex.Message + " (see log for full details)", new[] { "value" }) }); + } + } + + 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[] { "oldPassword" }) }); + } + if (membershipProvider.RequiresQuestionAndAnswer && passwordModel.Answer.IsNullOrWhiteSpace()) + { + //if the question answer is required but there isn't one, we cannot continue + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password cannot be changed without the password answer", new[] { "value" }) }); + } + + //lets try to get the old one so we can change it + try + { + var oldPassword = membershipProvider.GetPassword( + username, + membershipProvider.RequiresQuestionAndAnswer ? passwordModel.Answer : null); + + try + { + var result = membershipProvider.ChangePassword(username, oldPassword, passwordModel.NewPassword); + return result == false + ? Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password", new[] { "value" }) }) + : Attempt.Succeed(new PasswordChangedModel()); + } + catch (Exception ex1) + { + _logger.WarnWithException("Could not change member password", ex1); + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex1.Message + " (see log for full details)", new[] { "value" }) }); + } + + } + catch (Exception ex2) + { + _logger.WarnWithException("Could not retrieve member password", ex2); + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex2.Message + " (see log for full details)", new[] { "value" }) }); + } + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index 0039128e86..9164ea8630 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -404,7 +404,7 @@ namespace Umbraco.Web.Editors /// /// /// - public UserDisplay PostSaveUser(UserSave userSave) + public async Task PostSaveUser(UserSave userSave) { if (userSave == null) throw new ArgumentNullException("userSave"); @@ -460,7 +460,9 @@ namespace Umbraco.Web.Editors var resetPasswordValue = string.Empty; if (userSave.ChangePassword != null) { - var passwordChangeResult = PasswordChangeControllerHelper.ChangePassword(found, userSave.ChangePassword, ModelState, Members); + var passwordChanger = new PasswordChanger(Logger, Services.UserService); + + var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(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 diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 7a6a54f9e7..2d0daf1aa9 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -32,6 +32,7 @@ namespace Umbraco.Web.Install.InstallSteps _applicationContext = applicationContext; } + //TODO: Change all logic in this step to use ASP.NET Identity NOT MembershipProviders private MembershipProvider CurrentProvider { get diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index 81487c41d9..43d3c55c65 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Security; using Umbraco.Web.Models; using Umbraco.Web.PublishedCache; using Umbraco.Core.Cache; +using Umbraco.Web.Editors; using Umbraco.Web.Security.Providers; using MPE = global::Umbraco.Core.Security.MembershipProviderExtensions; @@ -655,128 +656,8 @@ namespace Umbraco.Web.Security /// public virtual Attempt ChangePassword(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider) { - // YES! It is completely insane how many options you have to take into account based on the membership provider. yikes! - - if (passwordModel == null) throw new ArgumentNullException("passwordModel"); - if (membershipProvider == null) throw new ArgumentNullException("membershipProvider"); - - //Are we resetting the password?? - if (passwordModel.Reset.HasValue && passwordModel.Reset.Value) - { - var canReset = membershipProvider.CanResetPassword(_applicationContext.Services.UserService); - if (canReset == false) - { - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password reset is not enabled", new[] { "resetPassword" }) }); - } - if (membershipProvider.RequiresQuestionAndAnswer && passwordModel.Answer.IsNullOrWhiteSpace()) - { - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password reset requires a password answer", new[] { "resetPassword" }) }); - } - //ok, we should be able to reset it - try - { - var newPass = membershipProvider.ResetPassword( - username, - membershipProvider.RequiresQuestionAndAnswer ? passwordModel.Answer : null); - - //return the generated pword - return Attempt.Succeed(new PasswordChangedModel { ResetPassword = newPass }); - } - catch (Exception ex) - { - LogHelper.WarnWithException("Could not reset member password", ex); - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not reset password, error: " + ex.Message + " (see log for full details)", new[] { "resetPassword" }) }); - } - } - - //we're not resetting it so we need to try to change it. - - if (passwordModel.NewPassword.IsNullOrWhiteSpace()) - { - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Cannot set an empty password", new[] { "value" }) }); - } - - //This is an edge case and is only necessary for backwards compatibility: - var umbracoBaseProvider = membershipProvider as MembershipProviderBase; - if (umbracoBaseProvider != null && umbracoBaseProvider.AllowManuallyChangingPassword) - { - //this provider allows manually changing the password without the old password, so we can just do it - try - { - var result = umbracoBaseProvider.ChangePassword(username, "", passwordModel.NewPassword); - return result == false - ? Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid username or password", new[] { "value" }) }) - : Attempt.Succeed(new PasswordChangedModel()); - } - catch (Exception ex) - { - LogHelper.WarnWithException("Could not change member password", ex); - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex.Message + " (see log for full details)", new[] { "value" }) }); - } - } - - //The provider does not support manually chaning the password but no old password supplied - need to return an error - 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[] { "oldPassword" }) }); - } - - if (passwordModel.OldPassword.IsNullOrWhiteSpace() == false) - { - //if an old password is suplied try to change it - - try - { - 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[] { "oldPassword" }) }) - : Attempt.Succeed(new PasswordChangedModel()); - } - catch (Exception ex) - { - LogHelper.WarnWithException("Could not change member password", ex); - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex.Message + " (see log for full details)", new[] { "value" }) }); - } - } - - 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[] { "oldPassword" }) }); - } - if (membershipProvider.RequiresQuestionAndAnswer && passwordModel.Answer.IsNullOrWhiteSpace()) - { - //if the question answer is required but there isn't one, we cannot continue - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password cannot be changed without the password answer", new[] { "value" }) }); - } - - //lets try to get the old one so we can change it - try - { - var oldPassword = membershipProvider.GetPassword( - username, - membershipProvider.RequiresQuestionAndAnswer ? passwordModel.Answer : null); - - try - { - var result = membershipProvider.ChangePassword(username, oldPassword, passwordModel.NewPassword); - return result == false - ? Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password", new[] { "value" }) }) - : Attempt.Succeed(new PasswordChangedModel()); - } - catch (Exception ex1) - { - LogHelper.WarnWithException("Could not change member password", ex1); - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex1.Message + " (see log for full details)", new[] { "value" }) }); - } - - } - catch (Exception ex2) - { - LogHelper.WarnWithException("Could not retrieve member password", ex2); - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, error: " + ex2.Message + " (see log for full details)", new[] { "value" }) }); - } + var passwordChanger = new PasswordChanger(_applicationContext.ProfilingLogger.Logger, _applicationContext.Services.UserService); + return passwordChanger.ChangePasswordWithMembershipProvider(username, passwordModel, membershipProvider); } /// diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index e4a29bd6cc..e46250bed8 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -170,10 +170,14 @@ namespace Umbraco.Web.Security /// /// /// + /// + /// This uses ASP.NET Identity to perform the validation + /// public virtual bool ValidateBackOfficeCredentials(string username, string password) { - var membershipProvider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider(); - return membershipProvider != null && membershipProvider.ValidateUser(username, password); + var backofficeuser = Mapper.Map(CurrentUser); + backofficeuser.UserName = username; + return UserManager.CheckPasswordAsync(backofficeuser, password).Result; } /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index afa4790262..29f4d04f26 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -333,7 +333,7 @@ - +