Updates the formHelper and streamlines the way form submission is handled, updates the member and user controller to use the new change password functionality in the Security object which uses all of the correct logic to update a password based on the membership provider. Gets the user's change password form working correctly with server validation. now just need to get the user membership provider to actually work.
This commit is contained in:
@@ -77,26 +77,17 @@ function userResource($q, $http, umbRequestHelper) {
|
||||
*
|
||||
* @description
|
||||
* Changes the current users password
|
||||
*
|
||||
* ##usage
|
||||
* <pre>
|
||||
* contentResource.getAll()
|
||||
* .then(function(userArray) {
|
||||
* var myUsers = userArray;
|
||||
* alert('they are here!');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @returns {Promise} resourcePromise object containing the user array.
|
||||
*
|
||||
*/
|
||||
changePassword: function (oldPassword, newPassword) {
|
||||
changePassword: function (changePasswordArgs) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"userApiBaseUrl",
|
||||
"PostChangePassword"),
|
||||
{ oldPassword: oldPassword, newPassword: newPassword }),
|
||||
changePasswordArgs),
|
||||
'Failed to change password');
|
||||
},
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @name umbraco.services.contentEditingHelper
|
||||
* @description A helper service for content/media/member controllers when editing/creating/saving content.
|
||||
**/
|
||||
function contentEditingHelper($location, $routeParams, notificationsService, serverValidationManager, dialogService) {
|
||||
function contentEditingHelper($location, $routeParams, notificationsService, serverValidationManager, dialogService, formHelper) {
|
||||
|
||||
return {
|
||||
|
||||
@@ -95,60 +95,6 @@ function contentEditingHelper($location, $routeParams, notificationsService, ser
|
||||
return changed;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.contentEditingHelper#handleValidationErrors
|
||||
* @methodOf umbraco.services.contentEditingHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* A function to handle the validation (modelState) errors collection which will happen on a 400 error indicating validation errors
|
||||
* It's worth noting that when a 400 occurs, the data is still saved just never published, though this depends on if the entity is a new
|
||||
* entity and whether or not the data fulfils the absolute basic requirements like having a mandatory Name.
|
||||
*/
|
||||
handleValidationErrors: function (allProps, modelState) {
|
||||
|
||||
//find the content property for the current error, for use in the loop below
|
||||
function findContentProp(props, propAlias) {
|
||||
return _.find(props, function (item) {
|
||||
return (item.alias === propAlias);
|
||||
});
|
||||
}
|
||||
|
||||
for (var e in modelState) {
|
||||
//the alias in model state can be in dot notation which indicates
|
||||
// * the first part is the content property alias
|
||||
// * the second part is the field to which the valiation msg is associated with
|
||||
//There will always be at least 2 parts since all model errors for properties are prefixed with "Properties"
|
||||
var parts = e.split(".");
|
||||
if (parts.length > 1) {
|
||||
var propertyAlias = parts[1];
|
||||
|
||||
//find the content property for the current error
|
||||
var contentProperty = findContentProp(allProps, propertyAlias);
|
||||
|
||||
if (contentProperty) {
|
||||
//if it contains 2 '.' then we will wire it up to a property's field
|
||||
if (parts.length > 2) {
|
||||
//add an error with a reference to the field for which the validation belongs too
|
||||
serverValidationManager.addPropertyError(contentProperty.alias, parts[2], modelState[e][0]);
|
||||
}
|
||||
else {
|
||||
//add a generic error for the property, no reference to a specific field
|
||||
serverValidationManager.addPropertyError(contentProperty.alias, "", modelState[e][0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
//the parts are only 1, this means its not a property but a native content property
|
||||
serverValidationManager.addFieldError(parts[0], modelState[e][0]);
|
||||
}
|
||||
|
||||
//add to notifications
|
||||
notificationsService.error("Validation", modelState[e][0]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.contentEditingHelper#handleSaveError
|
||||
@@ -174,7 +120,8 @@ function contentEditingHelper($location, $routeParams, notificationsService, ser
|
||||
//now we need to look through all the validation errors
|
||||
if (args.err.data && (args.err.data.ModelState)) {
|
||||
|
||||
this.handleValidationErrors(args.allNewProps, args.err.data.ModelState);
|
||||
//wire up the server validation errs
|
||||
formHelper.handleServerValidation(args.err.data.ModelState);
|
||||
|
||||
if (!args.redirectOnFailure || !this.redirectToCreatedContent(args.err.data.id, args.err.data.ModelState)) {
|
||||
//we are not redirecting because this is not new content, it is existing content. In this case
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* A utility class used to streamline how forms are developed, to ensure that validation is check and displayed consistently and to ensure that the correct events
|
||||
* fire when they need to.
|
||||
*/
|
||||
function formHelper(angularHelper, serverValidationManager, $timeout, notificationsService) {
|
||||
function formHelper(angularHelper, serverValidationManager, $timeout, notificationsService, dialogService) {
|
||||
return {
|
||||
|
||||
/**
|
||||
@@ -102,6 +102,86 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
|
||||
}
|
||||
|
||||
args.scope.$broadcast("formSubmitted", { scope: args.scope });
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.formHelper#handleError
|
||||
* @methodOf umbraco.services.formHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Needs to be called when a form submission fails, this will wire up all server validation errors in ModelState and
|
||||
* add the correct messages to the notifications. If a server error has occurred this will show a ysod.
|
||||
*
|
||||
* @param {object} err The error object returned from the http promise
|
||||
*/
|
||||
handleError: function (err) {
|
||||
//When the status is a 400 status with a custom header: X-Status-Reason: Validation failed, we have validation errors.
|
||||
//Otherwise the error is probably due to invalid data (i.e. someone mucking around with the ids or something).
|
||||
//Or, some strange server error
|
||||
if (err.status === 400) {
|
||||
//now we need to look through all the validation errors
|
||||
if (err.data && (err.data.ModelState)) {
|
||||
|
||||
//wire up the server validation errs
|
||||
this.handleServerValidation(err.data.ModelState);
|
||||
|
||||
//execute all server validation events and subscribers
|
||||
serverValidationManager.executeAndClearAllSubscriptions();
|
||||
}
|
||||
else {
|
||||
dialogService.ysodDialog(err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
dialogService.ysodDialog(err);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.formHelper#handleServerValidation
|
||||
* @methodOf umbraco.services.formHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* This wires up all of the server validation model state so that valServer and valServerField directives work
|
||||
*
|
||||
* @param {object} err The error object returned from the http promise
|
||||
*/
|
||||
handleServerValidation: function(modelState) {
|
||||
for (var e in modelState) {
|
||||
|
||||
//the alias in model state can be in dot notation which indicates
|
||||
// * the first part is the content property alias
|
||||
// * the second part is the field to which the valiation msg is associated with
|
||||
//There will always be at least 2 parts for properties since all model errors for properties are prefixed with "Properties"
|
||||
//If it is not prefixed with "Properties" that means the error is for a field of the object directly.
|
||||
|
||||
var parts = e.split(".");
|
||||
if (parts.length > 1) {
|
||||
var propertyAlias = parts[1];
|
||||
|
||||
//if it contains 2 '.' then we will wire it up to a property's field
|
||||
if (parts.length > 2) {
|
||||
//add an error with a reference to the field for which the validation belongs too
|
||||
serverValidationManager.addPropertyError(propertyAlias, parts[2], modelState[e][0]);
|
||||
}
|
||||
else {
|
||||
//add a generic error for the property, no reference to a specific field
|
||||
serverValidationManager.addPropertyError(propertyAlias, "", modelState[e][0]);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
//the parts are only 1, this means its not a property but a native content property
|
||||
serverValidationManager.addFieldError(parts[0], modelState[e][0]);
|
||||
}
|
||||
|
||||
//add to notifications
|
||||
notificationsService.error("Validation", modelState[e][0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,8 +44,11 @@ function serverValidationManager($timeout) {
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* This is primarily used for scenarios where the error collection needs to be persisted over a route change. Generally this
|
||||
* is when a content item (or any item) is created. The controller should call this method once the data is bound to the scope
|
||||
* This method needs to be called once all field and property errors are wired up.
|
||||
*
|
||||
* In some scenarios where the error collection needs to be persisted over a route change
|
||||
* (i.e. when a content item (or any item) is created and the route redirects to the editor)
|
||||
* the controller should call this method once the data is bound to the scope
|
||||
* so that any persisted validation errors are re-bound to their controls. Once they are re-binded this then clears the validation
|
||||
* colleciton so that if another route change occurs, the previously persisted validation errors are not re-bound to the new item.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<form novalidate name="passwordForm"
|
||||
ng-controller="Umbraco.Dashboard.StartupChangePasswordController"
|
||||
ng-submit="changePassword(profile)"
|
||||
ng-submit="changePassword()"
|
||||
val-form-manager>
|
||||
|
||||
<h3>Change password</h3>
|
||||
@@ -9,32 +9,10 @@
|
||||
<umb-pane>
|
||||
|
||||
<umb-editor model="changePasswordModel"></umb-editor>
|
||||
<!--<umb-control-group label="Old password">
|
||||
<input type="password" name="oldpass" ng-model="profile.oldPassword"
|
||||
required
|
||||
val-server />
|
||||
|
||||
<span class="help-inline" val-msg-for="oldpass" val-toggle-msg="required">Required</span>
|
||||
<span class="help-inline" val-msg-for="oldpass" val-toggle-msg="valServer"></span>
|
||||
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="New password">
|
||||
<input type="password" name="pass" ng-model="profile.newPassword" required/>
|
||||
|
||||
<span class="help-inline" val-msg-for="pass" val-toggle-msg="required">Required</span>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="Repeat new password">
|
||||
<input type="password" name="passcompare"
|
||||
val-compare="pass"
|
||||
ng-model="profile.repeatNewPassword" />
|
||||
|
||||
<small class="help-inline" val-msg-for="passcompare" val-toggle-msg="valCompare">Password does not match</small>
|
||||
</umb-control-group>-->
|
||||
|
||||
|
||||
<umb-control-group hideLabel="1">
|
||||
<button class="btn btn-primary">Change</button>
|
||||
</umb-control-group>
|
||||
|
||||
</umb-pane>
|
||||
</form>
|
||||
|
||||
@@ -84,9 +84,10 @@ function ChangePasswordDashboardController($scope, xmlhelper, $log, userResource
|
||||
|
||||
//create the initial model for change password property editor
|
||||
$scope.changePasswordModel = {
|
||||
alias: "password",
|
||||
alias: "_umb_password",
|
||||
view: "changepassword",
|
||||
config: {}
|
||||
config: {},
|
||||
value: {}
|
||||
};
|
||||
|
||||
//go get the config for the membership provider and add it to the model
|
||||
@@ -101,20 +102,18 @@ function ChangePasswordDashboardController($scope, xmlhelper, $log, userResource
|
||||
////this is the model we will pass to the service
|
||||
//$scope.profile = {};
|
||||
|
||||
$scope.changePassword = function(p) {
|
||||
$scope.changePassword = function() {
|
||||
|
||||
if (formHelper.submitForm({ scope: $scope })) {
|
||||
//userResource.changePassword(p.oldPassword, p.newPassword).then(function() {
|
||||
userResource.changePassword($scope.changePasswordModel.value).then(function() {
|
||||
|
||||
// formHelper.resetForm({ scope: $scope, notifications: data.notifications });
|
||||
|
||||
// //TODO: This is temporary - server validation will work automatically with the val-server directives.
|
||||
// $scope.passwordForm.$setValidity(true);
|
||||
//}, function () {
|
||||
// //TODO: This is temporary - server validation will work automatically with the val-server directives.
|
||||
// //this only happens if there is a wrong oldPassword sent along
|
||||
// $scope.passwordForm.oldpass.$setValidity("oldPassword", false);
|
||||
//});
|
||||
formHelper.resetForm({ scope: $scope, notifications: data.notifications });
|
||||
|
||||
}, function (err) {
|
||||
|
||||
formHelper.handleError(err);
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
describe('contentEditingHelper tests', function () {
|
||||
var contentEditingHelper, $routeParams, serverValidationManager, mocksUtils, notificationsService;
|
||||
var contentEditingHelper, $routeParams, serverValidationManager, mocksUtils, notificationsService, formHelper;
|
||||
|
||||
beforeEach(module('umbraco.services'));
|
||||
beforeEach(module('umbraco.mocks'));
|
||||
@@ -14,6 +14,7 @@ describe('contentEditingHelper tests', function () {
|
||||
serverValidationManager = $injector.get('serverValidationManager');
|
||||
mocksUtils = $injector.get('mocksUtils');
|
||||
notificationsService = $injector.get('notificationsService');
|
||||
formHelper = $injector.get('formHelper');
|
||||
}));
|
||||
|
||||
describe('handles http validation errors', function () {
|
||||
@@ -88,7 +89,7 @@ describe('contentEditingHelper tests', function () {
|
||||
var allProps = contentEditingHelper.getAllProps(content);
|
||||
|
||||
//act
|
||||
contentEditingHelper.handleValidationErrors(allProps, { Name: ["Required"] });
|
||||
formHelper.handleServerValidation({ Name: ["Required"] });
|
||||
|
||||
//assert
|
||||
expect(serverValidationManager.items.length).toBe(1);
|
||||
@@ -104,7 +105,7 @@ describe('contentEditingHelper tests', function () {
|
||||
var allProps = contentEditingHelper.getAllProps(content);
|
||||
|
||||
//act
|
||||
contentEditingHelper.handleValidationErrors(allProps, { "Property.bodyText": ["Required"] });
|
||||
formHelper.handleServerValidation({ "Property.bodyText": ["Required"] });
|
||||
|
||||
//assert
|
||||
expect(serverValidationManager.items.length).toBe(1);
|
||||
@@ -120,7 +121,7 @@ describe('contentEditingHelper tests', function () {
|
||||
var allProps = contentEditingHelper.getAllProps(content);
|
||||
|
||||
//act
|
||||
contentEditingHelper.handleValidationErrors(allProps, { "Property.bodyText.value": ["Required"] });
|
||||
formHelper.handleServerValidation({ "Property.bodyText.value": ["Required"] });
|
||||
|
||||
//assert
|
||||
expect(serverValidationManager.items.length).toBe(1);
|
||||
@@ -136,8 +137,7 @@ describe('contentEditingHelper tests', function () {
|
||||
var allProps = contentEditingHelper.getAllProps(content);
|
||||
|
||||
//act
|
||||
contentEditingHelper.handleValidationErrors(
|
||||
allProps,
|
||||
formHelper.handleServerValidation(
|
||||
{
|
||||
"Name": ["Required"],
|
||||
"UpdateDate": ["Invalid date"],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
@@ -250,132 +251,18 @@ namespace Umbraco.Web.Editors
|
||||
//password changes ?
|
||||
if (contentItem.Password == null) return null;
|
||||
|
||||
//Are we resetting the password??
|
||||
if (contentItem.Password.Reset.HasValue && contentItem.Password.Reset.Value)
|
||||
var passwordChangeResult = Security.ChangePassword(membershipUser.UserName, contentItem.Password, Membership.Provider);
|
||||
if (passwordChangeResult.Success)
|
||||
{
|
||||
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);
|
||||
|
||||
//return the generated pword
|
||||
return newPass;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WarnWithException<MemberController>("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));
|
||||
}
|
||||
}
|
||||
//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
|
||||
return passwordChangeResult.Result.ResetPassword;
|
||||
}
|
||||
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
|
||||
//it wasn't successful, so add the change error to the model state
|
||||
ModelState.AddPropertyError(
|
||||
passwordChangeResult.Result.ChangeError,
|
||||
string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
|
||||
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<MemberController>("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<MemberController>("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<MemberController>("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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Models.Mapping;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi;
|
||||
using legacyUser = umbraco.BusinessLogic.User;
|
||||
using System.Net.Http;
|
||||
using System.Collections.Specialized;
|
||||
@@ -60,18 +61,36 @@ namespace Umbraco.Web.Editors
|
||||
/// Changes the users password
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public HttpResponseMessage PostChangePassword(ChangePasswordModel data)
|
||||
{
|
||||
|
||||
var u = UmbracoContext.Security.CurrentUser;
|
||||
if (!UmbracoContext.Security.ValidateBackOfficeCredentials(u.Username, data.OldPassword))
|
||||
return new HttpResponseMessage(HttpStatusCode.Forbidden);
|
||||
/// <returns>
|
||||
/// If the password is being reset it will return the newly reset password, otherwise will return null;
|
||||
/// </returns>
|
||||
public string PostChangePassword(ChangingPasswordModel data)
|
||||
{
|
||||
var userProvider = Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider];
|
||||
if (userProvider == null)
|
||||
{
|
||||
throw new InvalidOperationException("No membership provider found with the name " + UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider);
|
||||
}
|
||||
|
||||
if(!UmbracoContext.Security.ChangePassword(data.OldPassword, data.NewPassword))
|
||||
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
|
||||
|
||||
return new HttpResponseMessage(HttpStatusCode.OK);
|
||||
//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 = Security.ChangePassword(Security.CurrentUser.Username, data, userProvider);
|
||||
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
|
||||
return passwordChangeResult.Result.ResetPassword;
|
||||
}
|
||||
|
||||
//it wasn't successful, so add the change error to the model state
|
||||
ModelState.AddPropertyError(
|
||||
passwordChangeResult.Result.ChangeError,
|
||||
string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+19
-2
@@ -1,11 +1,28 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// A model representing an attempt at changing a password
|
||||
/// </summary>
|
||||
public class PasswordChangedModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The error affiliated with the failing password changes, null if changing was successful
|
||||
/// </summary>
|
||||
public ValidationResult ChangeError { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the password was reset, this is the value it has been changed to
|
||||
/// </summary>
|
||||
public string ResetPassword { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A model representing the data required to set a member/user password depending on the provider installed.
|
||||
/// </summary>
|
||||
public class ChangePasswordModel
|
||||
public class ChangingPasswordModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The password value
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
public MemberSave()
|
||||
{
|
||||
Password = new ChangePasswordModel();
|
||||
Password = new ChangingPasswordModel();
|
||||
}
|
||||
|
||||
[DataMember(Name = "username", IsRequired = true)]
|
||||
@@ -24,6 +24,6 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
public string Email { get; set; }
|
||||
|
||||
[DataMember(Name = "password")]
|
||||
public ChangePasswordModel Password { get; set; }
|
||||
public ChangingPasswordModel Password { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
@@ -10,6 +11,7 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using umbraco;
|
||||
using umbraco.DataLayer;
|
||||
using umbraco.businesslogic.Exceptions;
|
||||
@@ -190,15 +192,119 @@ namespace Umbraco.Web.Security
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes password for a back office user
|
||||
/// Changes password for a member/user given the membership provider and the password change model
|
||||
/// </summary>
|
||||
/// <param name="oldpassword"></param>
|
||||
/// <param name="newpassword"></param>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="passwordModel"></param>
|
||||
/// <param name="membershipProvider"></param>
|
||||
/// <returns></returns>
|
||||
internal bool ChangePassword(string oldpassword, string newpassword)
|
||||
/// <remarks>
|
||||
/// YES! It is completely insane how many options you have to take into account based on the membership provider. yikes!
|
||||
/// </remarks>
|
||||
internal Attempt<PasswordChangedModel> ChangePassword(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider)
|
||||
{
|
||||
var membershipProvider = Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider];
|
||||
return membershipProvider.GetUser(CurrentUser.Username, true).ChangePassword(oldpassword, newpassword);
|
||||
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)
|
||||
{
|
||||
if (membershipProvider.EnablePasswordReset == 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<WebSecurity>("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" }) });
|
||||
}
|
||||
}
|
||||
if (passwordModel.NewPassword.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
//we're not resetting it so we need to try to change it.
|
||||
|
||||
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" }) });
|
||||
}
|
||||
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);
|
||||
if (result == false)
|
||||
{
|
||||
return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid username or password", new[] { "value" }) });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WarnWithException<WebSecurity>("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" }) });
|
||||
}
|
||||
}
|
||||
else 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" }) });
|
||||
}
|
||||
else 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" }) });
|
||||
}
|
||||
else
|
||||
{
|
||||
//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);
|
||||
if (result == false)
|
||||
{
|
||||
return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password", new[] { "value" }) });
|
||||
}
|
||||
}
|
||||
catch (Exception ex1)
|
||||
{
|
||||
LogHelper.WarnWithException<WebSecurity>("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<WebSecurity>("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" }) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//woot!
|
||||
return Attempt.Succeed(new PasswordChangedModel());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -313,7 +313,7 @@
|
||||
<Compile Include="Editors\MemberTypeController.cs" />
|
||||
<Compile Include="Editors\TagExtractor.cs" />
|
||||
<Compile Include="MembershipProviderExtensions.cs" />
|
||||
<Compile Include="Models\ContentEditing\ChangePasswordModel.cs" />
|
||||
<Compile Include="Models\ContentEditing\ChangingPasswordModel.cs" />
|
||||
<Compile Include="Models\ContentEditing\StyleSheet.cs" />
|
||||
<Compile Include="Editors\StylesheetController.cs" />
|
||||
<Compile Include="Models\ContentEditing\AuditLog.cs" />
|
||||
|
||||
Reference in New Issue
Block a user