mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Password change MU
closes #4624 - added user_id to password reset request - hide old password field - updated changePassword method to check permissions - updated changePassword method to work without oldPassword - fixed bug for errors shown as [Object object]
This commit is contained in:
parent
1626a8f292
commit
40c78493a3
5 changed files with 151 additions and 15 deletions
|
@ -52,6 +52,7 @@ var User = DS.Model.extend(NProgressSaveMixin, SelectiveSaveMixin, ValidationEng
|
|||
type: 'PUT',
|
||||
data: {
|
||||
password: [{
|
||||
user_id: this.get('id'),
|
||||
oldPassword: this.get('password'),
|
||||
newPassword: this.get('newPassword'),
|
||||
ne2Password: this.get('ne2Password')
|
||||
|
|
|
@ -96,11 +96,12 @@
|
|||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
|
||||
{{#unless view.isNotOwnProfile}}
|
||||
<div class="form-group">
|
||||
<label for="user-password-old">Old Password</label>
|
||||
{{input value=user.password type="password" id="user-password-old"}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="user-password-new">New Password</label>
|
||||
|
|
|
@ -323,17 +323,23 @@ users = {
|
|||
changePassword: function changePassword(object, options) {
|
||||
var oldPassword,
|
||||
newPassword,
|
||||
ne2Password;
|
||||
ne2Password,
|
||||
userId;
|
||||
|
||||
return utils.checkObject(object, 'password').then(function (checkedPasswordReset) {
|
||||
oldPassword = checkedPasswordReset.password[0].oldPassword;
|
||||
newPassword = checkedPasswordReset.password[0].newPassword;
|
||||
ne2Password = checkedPasswordReset.password[0].ne2Password;
|
||||
|
||||
return dataProvider.User.changePassword(oldPassword, newPassword, ne2Password, options).then(function () {
|
||||
return Promise.resolve({password: [{message: 'Password changed successfully.'}]});
|
||||
}).catch(function (error) {
|
||||
return Promise.reject(new errors.ValidationError(error.message));
|
||||
});
|
||||
userId = parseInt(checkedPasswordReset.password[0].user_id);
|
||||
}).then(function () {
|
||||
return canThis(options.context).edit.user(userId);
|
||||
}).then(function () {
|
||||
return dataProvider.User.changePassword(oldPassword, newPassword, ne2Password, userId, options);
|
||||
}).then(function () {
|
||||
return Promise.resolve({password: [{message: 'Password changed successfully.'}]});
|
||||
}).catch(function (error) {
|
||||
// return Promise.reject(new errors.ValidationError(error.message));
|
||||
return errors.handleAPIError(error, 'You do not have permission to change the password for this user');
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ var _ = require('lodash'),
|
|||
function validatePasswordLength(password) {
|
||||
try {
|
||||
if (!validator.isLength(password, 8)) {
|
||||
throw new Error('Your password must be at least 8 characters long.');
|
||||
throw new errors.ValidationError('Your password must be at least 8 characters long.');
|
||||
}
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
|
@ -687,25 +687,33 @@ User = ghostBookshelf.Model.extend({
|
|||
* @param {String} oldPassword
|
||||
* @param {String} newPassword
|
||||
* @param {String} ne2Password
|
||||
* @param {Integer} userId
|
||||
* @param {Object} options
|
||||
*/
|
||||
changePassword: function (oldPassword, newPassword, ne2Password, options) {
|
||||
changePassword: function (oldPassword, newPassword, ne2Password, userId, options) {
|
||||
var self = this,
|
||||
userid = options.context.user,
|
||||
user = null;
|
||||
|
||||
if (newPassword !== ne2Password) {
|
||||
return Promise.reject(new Error('Your new passwords do not match'));
|
||||
return Promise.reject(new errors.ValidationError('Your new passwords do not match'));
|
||||
}
|
||||
|
||||
if (userId === options.context.user && _.isEmpty(oldPassword)) {
|
||||
return Promise.reject(new errors.ValidationError('Password is required for this operation'));
|
||||
}
|
||||
|
||||
return validatePasswordLength(newPassword).then(function () {
|
||||
return self.forge({id: userid}).fetch({require: true});
|
||||
return self.forge({id: userId}).fetch({require: true});
|
||||
}).then(function (_user) {
|
||||
user = _user;
|
||||
return bcryptCompare(oldPassword, user.get('password'));
|
||||
if (userId === options.context.user) {
|
||||
return bcryptCompare(oldPassword, user.get('password'));
|
||||
}
|
||||
// if user is admin, password isn't compared
|
||||
return true;
|
||||
}).then(function (matched) {
|
||||
if (!matched) {
|
||||
return Promise.reject(new Error('Your password is incorrect'));
|
||||
return Promise.reject(new errors.ValidationError('Your password is incorrect'));
|
||||
}
|
||||
return bcryptGenSalt();
|
||||
}).then(function (salt) {
|
||||
|
|
|
@ -1019,4 +1019,124 @@ describe('Users API', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Change Password', function () {
|
||||
it('Owner can change own password', function (done) {
|
||||
var payload = {
|
||||
password: [{
|
||||
user_id: userIdFor.owner,
|
||||
oldPassword: 'Sl1m3rson',
|
||||
newPassword: 'newSl1m3rson',
|
||||
ne2Password: 'newSl1m3rson'
|
||||
}]
|
||||
};
|
||||
UserAPI.changePassword(payload, _.extend({}, context.owner, {id: userIdFor.owner}))
|
||||
.then(function (response) {
|
||||
response.password[0].message.should.eql('Password changed successfully.');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Owner can\'t change password with wrong oldPassword', function (done) {
|
||||
var payload = {
|
||||
password: [{
|
||||
user_id: userIdFor.owner,
|
||||
oldPassword: 'wrong',
|
||||
newPassword: 'Sl1m3rson',
|
||||
ne2Password: 'Sl1m3rson'
|
||||
}]
|
||||
};
|
||||
UserAPI.changePassword(payload, _.extend({}, context.owner, {id: userIdFor.owner}))
|
||||
.then(function () {
|
||||
done(new Error('Password change is not denied.'));
|
||||
}).catch(function (error) {
|
||||
error.type.should.eql('ValidationError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Owner can\'t change password without matching passwords', function (done) {
|
||||
var payload = {
|
||||
password: [{
|
||||
user_id: userIdFor.owner,
|
||||
oldPassword: 'Sl1m3rson',
|
||||
newPassword: 'Sl1m3rson1',
|
||||
ne2Password: 'Sl1m3rson2'
|
||||
}]
|
||||
};
|
||||
UserAPI.changePassword(payload, _.extend({}, context.owner, {id: userIdFor.owner}))
|
||||
.then(function () {
|
||||
done(new Error('Password change is not denied.'));
|
||||
}).catch(function (error) {
|
||||
error.type.should.eql('ValidationError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Owner can\'t change editor password without matching passwords', function (done) {
|
||||
var payload = {
|
||||
password: [{
|
||||
user_id: userIdFor.editor,
|
||||
newPassword: 'Sl1m3rson1',
|
||||
ne2Password: 'Sl1m3rson2'
|
||||
}]
|
||||
};
|
||||
UserAPI.changePassword(payload, _.extend({}, context.owner, {id: userIdFor.owner}))
|
||||
.then(function () {
|
||||
done(new Error('Password change is not denied.'));
|
||||
}).catch(function (error) {
|
||||
error.type.should.eql('ValidationError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Owner can\'t change editor password without short passwords', function (done) {
|
||||
var payload = {
|
||||
password: [{
|
||||
user_id: userIdFor.editor,
|
||||
newPassword: 'Sl',
|
||||
ne2Password: 'Sl'
|
||||
}]
|
||||
};
|
||||
UserAPI.changePassword(payload, _.extend({}, context.owner, {id: userIdFor.owner}))
|
||||
.then(function () {
|
||||
done(new Error('Password change is not denied.'));
|
||||
}).catch(function (error) {
|
||||
error.type.should.eql('ValidationError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Owner can change password for editor', function (done) {
|
||||
var payload = {
|
||||
password: [{
|
||||
user_id: userIdFor.editor,
|
||||
newPassword: 'newSl1m3rson',
|
||||
ne2Password: 'newSl1m3rson'
|
||||
}]
|
||||
};
|
||||
UserAPI.changePassword(payload, _.extend({}, context.owner, {id: userIdFor.owner}))
|
||||
.then(function (response) {
|
||||
response.password[0].message.should.eql('Password changed successfully.');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Editor can\'t change password for admin', function (done) {
|
||||
var payload = {
|
||||
password: [{
|
||||
user_id: userIdFor.admin,
|
||||
newPassword: 'newSl1m3rson',
|
||||
ne2Password: 'newSl1m3rson'
|
||||
}]
|
||||
};
|
||||
UserAPI.changePassword(payload, _.extend({}, context.editor, {id: userIdFor.editor}))
|
||||
.then(function () {
|
||||
done(new Error('Password change is not denied.'));
|
||||
}).catch(function (error) {
|
||||
error.type.should.eql('NoPermissionError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue