0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Expanded authentication test suite with cases for password reset flow

- Added missing endpoint coverage
- Minor fixes with formatting and validations uncovered by the test
- Added same test to v0.1 coverage
This commit is contained in:
Nazar Gargol 2019-07-30 22:48:59 +02:00
parent 3945e8a5ee
commit 956da204f2
5 changed files with 161 additions and 4 deletions

View file

@ -91,6 +91,9 @@ module.exports = {
}, },
generateResetToken: { generateResetToken: {
validation: {
docName: 'passwordreset'
},
permissions: true, permissions: true,
options: [ options: [
'email' 'email'
@ -101,7 +104,7 @@ module.exports = {
return auth.setup.assertSetupCompleted(true)(); return auth.setup.assertSetupCompleted(true)();
}) })
.then(() => { .then(() => {
return auth.passwordreset.generateToken(frame.data.email, api.settings); return auth.passwordreset.generateToken(frame.data.passwordreset[0].email, api.settings);
}) })
.then((token) => { .then((token) => {
return auth.passwordreset.sendResetNotification(token, api.mail); return auth.passwordreset.sendResetNotification(token, api.mail);
@ -113,7 +116,6 @@ module.exports = {
validation: { validation: {
docName: 'passwordreset', docName: 'passwordreset',
data: { data: {
token: {required: true},
newPassword: {required: true}, newPassword: {required: true},
ne2Password: {required: true} ne2Password: {required: true}
} }

View file

@ -25,6 +25,22 @@ module.exports = {
}; };
}, },
generateResetToken(data, apiConfig, frame) {
frame.response = {
passwordreset: [{
message: common.i18n.t('common.api.authentication.mail.checkEmailForInstructions')
}]
};
},
resetPassword(data, apiConfig, frame) {
frame.response = {
passwordreset: [{
message: common.i18n.t('common.api.authentication.mail.passwordChanged')
}]
};
},
acceptInvitation(data, apiConfig, frame) { acceptInvitation(data, apiConfig, frame) {
debug('acceptInvitation'); debug('acceptInvitation');

View file

@ -1,4 +1,5 @@
const Promise = require('bluebird'); const Promise = require('bluebird');
const validator = require('validator');
const debug = require('ghost-ignition').debug('api:v2:utils:validators:input:passwordreset'); const debug = require('ghost-ignition').debug('api:v2:utils:validators:input:passwordreset');
const common = require('../../../../../lib/common'); const common = require('../../../../../lib/common');
@ -13,5 +14,17 @@ module.exports = {
message: common.i18n.t('errors.models.user.newPasswordsDoNotMatch') message: common.i18n.t('errors.models.user.newPasswordsDoNotMatch')
})); }));
} }
},
generateResetToken(apiConfig, frame) {
debug('generateResetToken');
const email = frame.data.passwordreset[0].email;
if (typeof email !== 'string' || !validator.isEmail(email)) {
throw new common.errors.BadRequestError({
message: common.i18n.t('errors.api.authentication.invalidEmailReceived')
});
}
} }
}; };

View file

@ -32,7 +32,12 @@ describe('Authentication API', function () {
}); });
}); });
beforeEach(function () {
sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled');
});
afterEach(function () { afterEach(function () {
sinon.restore();
return testUtils.clearBruteData(); return testUtils.clearBruteData();
}); });
@ -271,6 +276,27 @@ describe('Authentication API', function () {
.expect(401); .expect(401);
}); });
it('reset password: send reset password', function () {
return request
.post(localUtils.API.getApiQuery('authentication/passwordreset/'))
.set('Origin', config.get('url'))
.set('Accept', 'application/json')
.send({
passwordreset: [{
email: user.email
}]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
var jsonResponse = res.body;
should.exist(jsonResponse.passwordreset[0].message);
jsonResponse.passwordreset[0].message.should.equal('Check your email for further instructions.');
mailService.GhostMailer.prototype.send.args[0][0].to.should.equal(user.email);
});
});
it('revoke token', function () { it('revoke token', function () {
return request return request
.post(localUtils.API.getApiQuery('authentication/revoke')) .post(localUtils.API.getApiQuery('authentication/revoke'))

View file

@ -1,15 +1,18 @@
const should = require('should'); const should = require('should');
const sinon = require('sinon'); const sinon = require('sinon');
const supertest = require('supertest'); const supertest = require('supertest');
const testUtils = require('../../../../utils/index');
const localUtils = require('./utils'); const localUtils = require('./utils');
const testUtils = require('../../../../utils/index');
const models = require('../../../../../server/models/index');
const security = require('../../../../../server/lib/security/index');
const settingsCache = require('../../../../../server/services/settings/cache');
const config = require('../../../../../server/config/index'); const config = require('../../../../../server/config/index');
const mailService = require('../../../../../server/services/mail/index'); const mailService = require('../../../../../server/services/mail/index');
let ghost = testUtils.startGhost; let ghost = testUtils.startGhost;
let request; let request;
describe.only('Authentication API v2', function () { describe('Authentication API v2', function () {
let ghostServer; let ghostServer;
describe('Blog setup', function () { describe('Blog setup', function () {
@ -209,4 +212,101 @@ describe.only('Authentication API v2', function () {
}); });
}); });
}); });
describe('Password reset', function () {
const user = testUtils.DataGenerator.forModel.users[0];
before(function () {
return ghost({forceStart: true})
.then(() => {
request = supertest.agent(config.get('url'));
})
.then(() => {
return localUtils.doAuth(request);
});
});
beforeEach(function () {
sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled');
});
afterEach(function () {
sinon.restore();
});
it('reset password', function (done) {
models.User.getOwnerUser(testUtils.context.internal)
.then(function (ownerUser) {
var token = security.tokens.resetToken.generateHash({
expires: Date.now() + (1000 * 60),
email: user.email,
dbHash: settingsCache.get('db_hash'),
password: ownerUser.get('password')
});
request.put(localUtils.API.getApiQuery('authentication/passwordreset'))
.set('Origin', config.get('url'))
.set('Accept', 'application/json')
.send({
passwordreset: [{
token: token,
newPassword: 'thisissupersafe',
ne2Password: 'thisissupersafe'
}]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
const jsonResponse = res.body;
should.exist(jsonResponse.passwordreset[0].message);
jsonResponse.passwordreset[0].message.should.equal('Password changed successfully.');
done();
});
})
.catch(done);
});
it('reset password: invalid token', function () {
return request
.put(localUtils.API.getApiQuery('authentication/passwordreset'))
.set('Origin', config.get('url'))
.set('Accept', 'application/json')
.send({
passwordreset: [{
token: 'invalid',
newPassword: 'thisissupersafe',
ne2Password: 'thisissupersafe'
}]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(401);
});
it('reset password: generate reset token', function () {
return request
.post(localUtils.API.getApiQuery('authentication/passwordreset'))
.set('Origin', config.get('url'))
.set('Accept', 'application/json')
.send({
passwordreset: [{
email: user.email
}]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
const jsonResponse = res.body;
should.exist(jsonResponse.passwordreset[0].message);
jsonResponse.passwordreset[0].message.should.equal('Check your email for further instructions.');
mailService.GhostMailer.prototype.send.args[0][0].to.should.equal(user.email);
});
});
});
}); });