mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-01 02:41:39 -05:00
Migrated authentication controller to v2 (#10950)
refs #10060 - Migrated authentication.resetPassword method to v2 - Migrated authentication.acceptInvitation method to v2 - Migrated authentication.setup method to v2 - Added missing test coverage for "setupUpdate" method - Migrated authentication.updateSetup method to v2 - Migrated authentication.isInvitation method to v2 - Migrated authentication.isSetup method to v2 - Removed unused 'setup.completed' event as it wasn's used anywhere in the system and has been complicating the logic unnecessarily - Without the event, it's possible to simplify sendNotification method to just use email address of the user - Added email sending check to v0.1 test suite - Refactored sendNotification method to just use email address as parameter - Renamed sendNotification to sendWelcomeMail - The only thing the method does now is sending welcome mail, so new naming seems natural :)
This commit is contained in:
commit
27bf453792
15 changed files with 763 additions and 21 deletions
|
@ -187,5 +187,15 @@ module.exports = {
|
|||
changePassword() {
|
||||
debug('validate changePassword');
|
||||
return this.add(...arguments);
|
||||
},
|
||||
|
||||
resetPassword() {
|
||||
debug('validate resetPassword');
|
||||
return this.add(...arguments);
|
||||
},
|
||||
|
||||
setup() {
|
||||
debug('validate setup');
|
||||
return this.add(...arguments);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -290,7 +290,8 @@ authentication = {
|
|||
}
|
||||
|
||||
function sendNotification(setupUser) {
|
||||
return auth.setup.sendNotification(setupUser, mailAPI);
|
||||
return auth.setup.sendWelcomeEmail(setupUser.email, mailAPI)
|
||||
.then(() => setupUser);
|
||||
}
|
||||
|
||||
function formatResponse(setupUser) {
|
||||
|
|
186
core/server/api/v2/authentication.js
Normal file
186
core/server/api/v2/authentication.js
Normal file
|
@ -0,0 +1,186 @@
|
|||
const api = require('./index');
|
||||
const config = require('../../config');
|
||||
const common = require('../../lib/common');
|
||||
const web = require('../../web');
|
||||
const models = require('../../models');
|
||||
const auth = require('../../services/auth');
|
||||
const invitations = require('../../services/invitations');
|
||||
|
||||
module.exports = {
|
||||
docName: 'authentication',
|
||||
|
||||
setup: {
|
||||
statusCode: 201,
|
||||
permissions: false,
|
||||
validation: {
|
||||
docName: 'setup'
|
||||
},
|
||||
query(frame) {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return auth.setup.assertSetupCompleted(false)();
|
||||
})
|
||||
.then(() => {
|
||||
const setupDetails = {
|
||||
name: frame.data.setup[0].name,
|
||||
email: frame.data.setup[0].email,
|
||||
password: frame.data.setup[0].password,
|
||||
blogTitle: frame.data.setup[0].blogTitle,
|
||||
status: 'active'
|
||||
};
|
||||
|
||||
return auth.setup.setupUser(setupDetails);
|
||||
})
|
||||
.then((data) => {
|
||||
return auth.setup.doSettings(data, api.settings);
|
||||
})
|
||||
.then((user) => {
|
||||
return auth.setup.sendWelcomeEmail(user.get('email'), api.mail)
|
||||
.then(() => user);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
updateSetup: {
|
||||
permissions: (frame) => {
|
||||
return models.User.findOne({role: 'Owner', status: 'all'})
|
||||
.then((owner) => {
|
||||
if (owner.id !== frame.options.context.user) {
|
||||
throw new common.errors.NoPermissionError({message: common.i18n.t('errors.api.authentication.notTheBlogOwner')});
|
||||
}
|
||||
});
|
||||
},
|
||||
validation: {
|
||||
docName: 'setup'
|
||||
},
|
||||
query(frame) {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return auth.setup.assertSetupCompleted(true)();
|
||||
})
|
||||
.then(() => {
|
||||
const setupDetails = {
|
||||
name: frame.data.setup[0].name,
|
||||
email: frame.data.setup[0].email,
|
||||
password: frame.data.setup[0].password,
|
||||
blogTitle: frame.data.setup[0].blogTitle,
|
||||
status: 'active'
|
||||
};
|
||||
|
||||
return auth.setup.setupUser(setupDetails);
|
||||
})
|
||||
.then((data) => {
|
||||
return auth.setup.doSettings(data, api.settings);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
isSetup: {
|
||||
permissions: false,
|
||||
query() {
|
||||
return auth.setup.checkIsSetup()
|
||||
.then((isSetup) => {
|
||||
return {
|
||||
status: isSetup,
|
||||
// Pre-populate from config if, and only if the values exist in config.
|
||||
title: config.title || undefined,
|
||||
name: config.user_name || undefined,
|
||||
email: config.user_email || undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
generateResetToken: {
|
||||
validation: {
|
||||
docName: 'passwordreset'
|
||||
},
|
||||
permissions: true,
|
||||
options: [
|
||||
'email'
|
||||
],
|
||||
query(frame) {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return auth.setup.assertSetupCompleted(true)();
|
||||
})
|
||||
.then(() => {
|
||||
return auth.passwordreset.generateToken(frame.data.passwordreset[0].email, api.settings);
|
||||
})
|
||||
.then((token) => {
|
||||
return auth.passwordreset.sendResetNotification(token, api.mail);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
resetPassword: {
|
||||
validation: {
|
||||
docName: 'passwordreset',
|
||||
data: {
|
||||
newPassword: {required: true},
|
||||
ne2Password: {required: true}
|
||||
}
|
||||
},
|
||||
permissions: false,
|
||||
options: [
|
||||
'ip'
|
||||
],
|
||||
query(frame) {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return auth.setup.assertSetupCompleted(true)();
|
||||
})
|
||||
.then(() => {
|
||||
return auth.passwordreset.extractTokenParts(frame);
|
||||
})
|
||||
.then((params) => {
|
||||
return auth.passwordreset.protectBruteForce(params);
|
||||
})
|
||||
.then(({options, tokenParts}) => {
|
||||
options = Object.assign(options, {context: {internal: true}});
|
||||
return auth.passwordreset.doReset(options, tokenParts, api.settings)
|
||||
.then((params) => {
|
||||
web.shared.middlewares.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
|
||||
return params;
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
acceptInvitation: {
|
||||
validation: {
|
||||
docName: 'invitations'
|
||||
},
|
||||
permissions: false,
|
||||
query(frame) {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return auth.setup.assertSetupCompleted(true)();
|
||||
})
|
||||
.then(() => {
|
||||
return invitations.accept(frame.data);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
isInvitation: {
|
||||
data: [
|
||||
'email'
|
||||
],
|
||||
validation: {
|
||||
docName: 'invitations'
|
||||
},
|
||||
permissions: false,
|
||||
query(frame) {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return auth.setup.assertSetupCompleted(true)();
|
||||
})
|
||||
.then(() => {
|
||||
const email = frame.data.email;
|
||||
|
||||
return models.Invite.findOne({email: email, status: 'sent'}, frame.options);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
|
@ -6,6 +6,10 @@ module.exports = {
|
|||
return shared.http;
|
||||
},
|
||||
|
||||
get authentication() {
|
||||
return shared.pipeline(require('./authentication'), localUtils);
|
||||
},
|
||||
|
||||
get db() {
|
||||
return shared.pipeline(require('./db'), localUtils);
|
||||
},
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
const common = require('../../../../../lib/common');
|
||||
const mapper = require('./utils/mapper');
|
||||
const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:authentication');
|
||||
|
||||
module.exports = {
|
||||
setup(user, apiConfig, frame) {
|
||||
frame.response = {
|
||||
users: [
|
||||
mapper.mapUser(user, {options: {context: {internal: true}}})
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
updateSetup(user, apiConfig, frame) {
|
||||
frame.response = {
|
||||
users: [
|
||||
mapper.mapUser(user, {options: {context: {internal: true}}})
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
isSetup(data, apiConfig, frame) {
|
||||
frame.response = {
|
||||
setup: [data]
|
||||
};
|
||||
},
|
||||
|
||||
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) {
|
||||
debug('acceptInvitation');
|
||||
|
||||
frame.response = {
|
||||
invitation: [
|
||||
{message: common.i18n.t('common.api.authentication.mail.invitationAccepted')}
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
isInvitation(data, apiConfig, frame) {
|
||||
debug('acceptInvitation');
|
||||
|
||||
frame.response = {
|
||||
invitation: [{
|
||||
valid: !!data
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
|
@ -3,6 +3,10 @@ module.exports = {
|
|||
return require('./all');
|
||||
},
|
||||
|
||||
get authentication() {
|
||||
return require('./authentication');
|
||||
},
|
||||
|
||||
get db() {
|
||||
return require('./db');
|
||||
},
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
module.exports = {
|
||||
get passwordreset() {
|
||||
return require('./passwordreset');
|
||||
},
|
||||
|
||||
get setup() {
|
||||
return require('./setup');
|
||||
},
|
||||
|
||||
get posts() {
|
||||
return require('./posts');
|
||||
},
|
||||
|
@ -11,6 +19,10 @@ module.exports = {
|
|||
return require('./invites');
|
||||
},
|
||||
|
||||
get invitations() {
|
||||
return require('./invitations');
|
||||
},
|
||||
|
||||
get settings() {
|
||||
return require('./settings');
|
||||
},
|
||||
|
|
40
core/server/api/v2/utils/validators/input/invitations.js
Normal file
40
core/server/api/v2/utils/validators/input/invitations.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
const Promise = require('bluebird');
|
||||
const validator = require('validator');
|
||||
const debug = require('ghost-ignition').debug('api:v2:utils:validators:input:invitation');
|
||||
const common = require('../../../../../lib/common');
|
||||
|
||||
module.exports = {
|
||||
acceptInvitation(apiConfig, frame) {
|
||||
debug('acceptInvitation');
|
||||
|
||||
const data = frame.data.invitation[0];
|
||||
|
||||
if (!data.token) {
|
||||
return Promise.reject(new common.errors.ValidationError({message: common.i18n.t('errors.api.authentication.noTokenProvided')}));
|
||||
}
|
||||
|
||||
if (!data.email) {
|
||||
return Promise.reject(new common.errors.ValidationError({message: common.i18n.t('errors.api.authentication.noEmailProvided')}));
|
||||
}
|
||||
|
||||
if (!data.password) {
|
||||
return Promise.reject(new common.errors.ValidationError({message: common.i18n.t('errors.api.authentication.noPasswordProvided')}));
|
||||
}
|
||||
|
||||
if (!data.name) {
|
||||
return Promise.reject(new common.errors.ValidationError({message: common.i18n.t('errors.api.authentication.noNameProvided')}));
|
||||
}
|
||||
},
|
||||
|
||||
isInvitation(apiConfig, frame) {
|
||||
debug('isInvitation');
|
||||
|
||||
const email = frame.data.email;
|
||||
|
||||
if (typeof email !== 'string' || !validator.isEmail(email)) {
|
||||
throw new common.errors.BadRequestError({
|
||||
message: common.i18n.t('errors.api.authentication.invalidEmailReceived')
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
30
core/server/api/v2/utils/validators/input/passwordreset.js
Normal file
30
core/server/api/v2/utils/validators/input/passwordreset.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
const Promise = require('bluebird');
|
||||
const validator = require('validator');
|
||||
const debug = require('ghost-ignition').debug('api:v2:utils:validators:input:passwordreset');
|
||||
const common = require('../../../../../lib/common');
|
||||
|
||||
module.exports = {
|
||||
resetPassword(apiConfig, frame) {
|
||||
debug('resetPassword');
|
||||
|
||||
const data = frame.data.passwordreset[0];
|
||||
|
||||
if (data.newPassword !== data.ne2Password) {
|
||||
return Promise.reject(new common.errors.ValidationError({
|
||||
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')
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
12
core/server/api/v2/utils/validators/input/setup.js
Normal file
12
core/server/api/v2/utils/validators/input/setup.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
const debug = require('ghost-ignition').debug('api:v2:utils:validators:input:updateSetup');
|
||||
const common = require('../../../../../lib/common');
|
||||
|
||||
module.exports = {
|
||||
updateSetup(apiConfig, frame) {
|
||||
debug('resetPassword');
|
||||
|
||||
if (!frame.options.context || !frame.options.context.user) {
|
||||
throw new common.errors.NoPermissionError({message: common.i18n.t('errors.api.authentication.notTheBlogOwner')});
|
||||
}
|
||||
}
|
||||
};
|
|
@ -82,18 +82,16 @@ async function doSettings(data, settingsAPI) {
|
|||
return user;
|
||||
}
|
||||
|
||||
function sendNotification(setupUser, mailAPI) {
|
||||
const data = {
|
||||
ownerEmail: setupUser.email
|
||||
};
|
||||
|
||||
common.events.emit('setup.completed', setupUser);
|
||||
|
||||
function sendWelcomeEmail(email, mailAPI) {
|
||||
if (config.get('sendWelcomeEmail')) {
|
||||
const data = {
|
||||
ownerEmail: email
|
||||
};
|
||||
|
||||
return mail.utils.generateContent({data: data, template: 'welcome'})
|
||||
.then((content) => {
|
||||
const message = {
|
||||
to: setupUser.email,
|
||||
to: email,
|
||||
subject: common.i18n.t('common.api.authentication.mail.yourNewGhostBlog'),
|
||||
html: content.html,
|
||||
text: content.text
|
||||
|
@ -110,11 +108,8 @@ function sendNotification(setupUser, mailAPI) {
|
|||
err.context = common.i18n.t('errors.api.authentication.unableToSendWelcomeEmail');
|
||||
common.logging.error(err);
|
||||
});
|
||||
})
|
||||
.return(setupUser);
|
||||
});
|
||||
}
|
||||
|
||||
return setupUser;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -122,5 +117,5 @@ module.exports = {
|
|||
assertSetupCompleted: assertSetupCompleted,
|
||||
setupUser: setupUser,
|
||||
doSettings: doSettings,
|
||||
sendNotification: sendNotification
|
||||
sendWelcomeEmail: sendWelcomeEmail
|
||||
};
|
||||
|
|
|
@ -184,14 +184,14 @@ module.exports = function apiRoutes() {
|
|||
router.post('/authentication/passwordreset',
|
||||
shared.middlewares.brute.globalReset,
|
||||
shared.middlewares.brute.userReset,
|
||||
api.http(api.authentication.generateResetToken)
|
||||
http(apiv2.authentication.generateResetToken)
|
||||
);
|
||||
router.put('/authentication/passwordreset', shared.middlewares.brute.globalBlock, api.http(api.authentication.resetPassword));
|
||||
router.post('/authentication/invitation', api.http(api.authentication.acceptInvitation));
|
||||
router.get('/authentication/invitation', api.http(api.authentication.isInvitation));
|
||||
router.post('/authentication/setup', api.http(api.authentication.setup));
|
||||
router.put('/authentication/setup', mw.authAdminApi, api.http(api.authentication.updateSetup));
|
||||
router.get('/authentication/setup', api.http(api.authentication.isSetup));
|
||||
router.put('/authentication/passwordreset', shared.middlewares.brute.globalBlock, http(apiv2.authentication.resetPassword));
|
||||
router.post('/authentication/invitation', http(apiv2.authentication.acceptInvitation));
|
||||
router.get('/authentication/invitation', http(apiv2.authentication.isInvitation));
|
||||
router.post('/authentication/setup', http(apiv2.authentication.setup));
|
||||
router.put('/authentication/setup', mw.authAdminApi, http(apiv2.authentication.updateSetup));
|
||||
router.get('/authentication/setup', http(apiv2.authentication.isSetup));
|
||||
|
||||
// ## Images
|
||||
router.post('/images/upload',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
supertest = require('supertest'),
|
||||
testUtils = require('../../../utils/index'),
|
||||
localUtils = require('./utils'),
|
||||
|
@ -9,6 +10,7 @@ var should = require('should'),
|
|||
config = require('../../../../server/config/index'),
|
||||
security = require('../../../../server/lib/security/index'),
|
||||
settingsCache = require('../../../../server/services/settings/cache'),
|
||||
mailService = require('../../../../server/services/mail/index'),
|
||||
ghost = testUtils.startGhost,
|
||||
request;
|
||||
|
||||
|
@ -30,7 +32,12 @@ describe('Authentication API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sinon.restore();
|
||||
return testUtils.clearBruteData();
|
||||
});
|
||||
|
||||
|
@ -269,6 +276,27 @@ describe('Authentication API', function () {
|
|||
.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 () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('authentication/revoke'))
|
||||
|
@ -300,6 +328,14 @@ describe('Authentication API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('is setup? no', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery('authentication/setup'))
|
||||
|
@ -337,6 +373,9 @@ describe('Authentication API', function () {
|
|||
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
|
||||
newUser.name.should.equal('test user');
|
||||
newUser.email.should.equal('test@example.com');
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.true();
|
||||
mailService.GhostMailer.prototype.send.args[0][0].to.should.equal('test@example.com');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -366,6 +405,39 @@ describe('Authentication API', function () {
|
|||
.expect('Content-Type', /json/)
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('update setup', function () {
|
||||
return localUtils.doAuth(request)
|
||||
.then((ownerAccessToken) => {
|
||||
return request
|
||||
.put(localUtils.API.getApiQuery('authentication/setup'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
setup: [{
|
||||
name: 'test user edit',
|
||||
email: 'test-edited@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
})
|
||||
.then((res) => {
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
should.not.exist(jsonResponse.meta);
|
||||
|
||||
jsonResponse.users.should.have.length(1);
|
||||
localUtils.API.checkResponse(jsonResponse.users[0], 'user');
|
||||
|
||||
const newUser = jsonResponse.users[0];
|
||||
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
|
||||
newUser.name.should.equal('test user edit');
|
||||
newUser.email.should.equal('test-edited@example.com');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invitation', function () {
|
||||
|
|
312
core/test/regression/api/v2/admin/authentication_spec.js
Normal file
312
core/test/regression/api/v2/admin/authentication_spec.js
Normal file
|
@ -0,0 +1,312 @@
|
|||
const should = require('should');
|
||||
const sinon = require('sinon');
|
||||
const supertest = require('supertest');
|
||||
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 mailService = require('../../../../../server/services/mail/index');
|
||||
|
||||
let ghost = testUtils.startGhost;
|
||||
let request;
|
||||
|
||||
describe('Authentication API v2', function () {
|
||||
let ghostServer;
|
||||
|
||||
describe('Blog setup', function () {
|
||||
before(function () {
|
||||
return ghost({forceStart: true})
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('is setup? no', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery('authentication/setup'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
res.body.setup[0].status.should.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
it('complete setup', function () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('authentication/setup'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
setup: [{
|
||||
name: 'test user',
|
||||
email: 'test@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(201)
|
||||
.then((res) => {
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
should.not.exist(jsonResponse.meta);
|
||||
|
||||
jsonResponse.users.should.have.length(1);
|
||||
localUtils.API.checkResponse(jsonResponse.users[0], 'user');
|
||||
|
||||
const newUser = jsonResponse.users[0];
|
||||
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
|
||||
newUser.name.should.equal('test user');
|
||||
newUser.email.should.equal('test@example.com');
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.true();
|
||||
mailService.GhostMailer.prototype.send.args[0][0].to.should.equal('test@example.com');
|
||||
});
|
||||
});
|
||||
|
||||
it('is setup? yes', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery('authentication/setup'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
res.body.setup[0].status.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('complete setup again', function () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('authentication/setup'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
setup: [{
|
||||
name: 'test user',
|
||||
email: 'test-leo@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('update setup', function () {
|
||||
return localUtils.doAuth(request)
|
||||
.then(() => {
|
||||
return request
|
||||
.put(localUtils.API.getApiQuery('authentication/setup'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
setup: [{
|
||||
name: 'test user edit',
|
||||
email: 'test-edit@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
})
|
||||
.then((res) => {
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
should.not.exist(jsonResponse.meta);
|
||||
|
||||
jsonResponse.users.should.have.length(1);
|
||||
localUtils.API.checkResponse(jsonResponse.users[0], 'user');
|
||||
|
||||
const newUser = jsonResponse.users[0];
|
||||
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
|
||||
newUser.name.should.equal('test user edit');
|
||||
newUser.email.should.equal('test-edit@example.com');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invitation', function () {
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
|
||||
// simulates blog setup (initialises the owner)
|
||||
return localUtils.doAuth(request, 'invites');
|
||||
});
|
||||
});
|
||||
|
||||
it('check invite with invalid email', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery('authentication/invitation?email=invalidemail'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('check valid invite', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery(`authentication/invitation?email=${testUtils.DataGenerator.forKnex.invites[0].email}`))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
res.body.invitation[0].valid.should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('check invalid invite', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery(`authentication/invitation?email=notinvited@example.org`))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
res.body.invitation[0].valid.should.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('try to accept without invite', function () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('authentication/invitation'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
invitation: [{
|
||||
token: 'lul11111',
|
||||
password: 'lel123456',
|
||||
email: 'not-invited@example.org',
|
||||
name: 'not invited'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('try to accept with invite', function () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('authentication/invitation'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
invitation: [{
|
||||
token: testUtils.DataGenerator.forKnex.invites[0].token,
|
||||
password: '12345678910',
|
||||
email: testUtils.DataGenerator.forKnex.invites[0].email,
|
||||
name: 'invited'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
res.body.invitation[0].message.should.equal('Invitation accepted.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -37,6 +37,7 @@ const expectedProperties = {
|
|||
.without('locale')
|
||||
.without('ghost_auth_access_token')
|
||||
.without('ghost_auth_id')
|
||||
.concat('url')
|
||||
,
|
||||
tag: _(schema.tags)
|
||||
.keys()
|
||||
|
|
Loading…
Add table
Reference in a new issue