From a2a0ba9023d741bd949260f1c586b6769202cfc2 Mon Sep 17 00:00:00 2001 From: Austin Burdine Date: Tue, 26 May 2015 22:14:43 -0600 Subject: [PATCH] adds put route to authentication setup closes #5342 - adds put version of authentication/setup that allows for updating of owner/settings values - doesn't send welcome email - adds tests for new put route --- core/server/api/authentication.js | 108 +++++++++-------- core/server/middleware/middleware.js | 3 +- core/server/routes/api.js | 1 + .../api/api_authentication_spec.js | 111 +++++++++++++++++- 4 files changed, 171 insertions(+), 52 deletions(-) diff --git a/core/server/api/authentication.js b/core/server/api/authentication.js index 7958a309a8..584fc0d7bc 100644 --- a/core/server/api/authentication.js +++ b/core/server/api/authentication.js @@ -9,6 +9,45 @@ var _ = require('lodash'), config = require('../config'), authentication; +function setupTasks(object) { + var setupUser, + internal = {context: {internal: true}}; + + return utils.checkObject(object, 'setup').then(function (checkedSetupData) { + setupUser = { + name: checkedSetupData.setup[0].name, + email: checkedSetupData.setup[0].email, + password: checkedSetupData.setup[0].password, + blogTitle: checkedSetupData.setup[0].blogTitle, + status: 'active' + }; + + return dataProvider.User.findOne({role: 'Owner', status: 'all'}); + }).then(function (ownerUser) { + if (ownerUser) { + return dataProvider.User.setup(setupUser, _.extend({id: ownerUser.id}, internal)); + } else { + return dataProvider.Role.findOne({name: 'Owner'}).then(function (ownerRole) { + setupUser.roles = [ownerRole.id]; + return dataProvider.User.add(setupUser, internal); + }); + } + }).then(function (user) { + var userSettings = []; + + // Handles the additional values set by the setup screen. + if (!_.isEmpty(setupUser.blogTitle)) { + userSettings.push({key: 'title', value: setupUser.blogTitle}); + userSettings.push({key: 'description', value: 'Thoughts, stories and ideas.'}); + } + + setupUser = user.toJSON(internal); + return settings.edit({settings: userSettings}, {context: {user: setupUser.id}}); + }).then(function () { + return Promise.resolve(setupUser); + }); +} + /** * ## Authentication API Methods * @@ -153,7 +192,7 @@ authentication = { * @param {string} options.email The email to check for an invitation on * @returns {Promise(Invitation}} An invitation status */ - isInvitation: function (options) { + isInvitation: function isInvitation(options) { return authentication.isSetup().then(function (result) { var setup = result.setup[0].status; @@ -175,7 +214,7 @@ authentication = { }); }, - isSetup: function () { + isSetup: function isSetup() { return dataProvider.User.query(function (qb) { qb.whereIn('status', ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked']); }).fetch().then(function (users) { @@ -187,9 +226,8 @@ authentication = { }); }, - setup: function (object) { - var setupUser, - internal = {context: {internal: true}}; + setup: function setup(object) { + var setupUser; return authentication.isSetup().then(function (result) { var setup = result.setup[0].status; @@ -198,37 +236,10 @@ authentication = { return Promise.reject(new errors.NoPermissionError('Setup has already been completed.')); } - return utils.checkObject(object, 'setup'); - }).then(function (checkedSetupData) { - setupUser = { - name: checkedSetupData.setup[0].name, - email: checkedSetupData.setup[0].email, - password: checkedSetupData.setup[0].password, - blogTitle: checkedSetupData.setup[0].blogTitle, - status: 'active' - }; + return setupTasks(object); + }).then(function (result) { + setupUser = result; - return dataProvider.User.findOne({role: 'Owner', status: 'all'}); - }).then(function (ownerUser) { - if (ownerUser) { - return dataProvider.User.setup(setupUser, _.extend({id: ownerUser.id}, internal)); - } else { - return dataProvider.Role.findOne({name: 'Owner'}).then(function (ownerRole) { - setupUser.roles = [ownerRole.id]; - return dataProvider.User.add(setupUser, internal); - }); - } - }).then(function (user) { - var userSettings = []; - - // Handles the additional values set by the setup screen. - if (!_.isEmpty(setupUser.blogTitle)) { - userSettings.push({key: 'title', value: setupUser.blogTitle}); - userSettings.push({key: 'description', value: 'Thoughts, stories and ideas.'}); - } - setupUser = user.toJSON(internal); - return settings.edit({settings: userSettings}, {context: {user: setupUser.id}}); - }).then(function () { var data = { ownerEmail: setupUser.email }; @@ -260,22 +271,21 @@ authentication = { }); }, - revoke: function (object) { - var token; - - if (object.token_type_hint && object.token_type_hint === 'access_token') { - token = dataProvider.Accesstoken; - } else if (object.token_type_hint && object.token_type_hint === 'refresh_token') { - token = dataProvider.Refreshtoken; - } else { - return errors.BadRequestError('Invalid token_type_hint given.'); + updateSetup: function updateSetup(object, options) { + if (!options.context || !options.context.user) { + return Promise.reject(new errors.NoPermissionError('You are not logged in.')); } - return token.destroyByToken({token: object.token}).then(function () { - return Promise.resolve({token: object.token}); - }, function () { - // On error we still want a 200. See https://tools.ietf.org/html/rfc7009#page-5 - return Promise.resolve({token: object.token, error: 'Invalid token provided'}); + return dataProvider.User.findOne({role: 'Owner', status: 'all'}).then(function (result) { + var user = result.toJSON(); + + if (user.id !== options.context.user) { + return Promise.reject(new errors.NoPermissionError('You are not the blog owner.')); + } + + return setupTasks(object); + }).then(function (result) { + return Promise.resolve({users: [result]}); }); } }; diff --git a/core/server/middleware/middleware.js b/core/server/middleware/middleware.js index d5bb4fd495..fbf9472f5a 100644 --- a/core/server/middleware/middleware.js +++ b/core/server/middleware/middleware.js @@ -111,7 +111,8 @@ middleware = { }); if (subPath.indexOf('/ghost/api/') === 0 - && path.indexOf('/ghost/api/v0.1/authentication/') !== 0) { + && (path.indexOf('/ghost/api/v0.1/authentication/') !== 0 + || (path.indexOf('/ghost/api/v0.1/authentication/') === 0 && req.method === 'PUT'))) { return passport.authenticate('bearer', {session: false, failWithError: true}, function authenticate(err, user, info) { if (err) { diff --git a/core/server/routes/api.js b/core/server/routes/api.js index a17accef41..9acd168a7c 100644 --- a/core/server/routes/api.js +++ b/core/server/routes/api.js @@ -77,6 +77,7 @@ apiRoutes = function apiRoutes(middleware) { 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', api.http(api.authentication.updateSetup)); router.get('/authentication/setup', api.http(api.authentication.isSetup)); router.post('/authentication/token', middleware.spamPrevention.signin, diff --git a/core/test/integration/api/api_authentication_spec.js b/core/test/integration/api/api_authentication_spec.js index 1b3a644bfc..f753151f0e 100644 --- a/core/test/integration/api/api_authentication_spec.js +++ b/core/test/integration/api/api_authentication_spec.js @@ -7,7 +7,8 @@ var testUtils = require('../../utils'), // Stuff we are testing mail = rewire('../../../server/api/mail'), - AuthAPI = require('../../../server/api/authentication'); + AuthAPI = require('../../../server/api/authentication'), + context = testUtils.context; describe('Authentication API', function () { // Keep the DB clean @@ -81,7 +82,7 @@ describe('Authentication API', function () { name: 'test user', email: 'test@example.com', password: 'areallygoodpassword', - title: 'a test blog' + blogTitle: 'a test blog' }; AuthAPI.setup({setup: [setupData]}).then(function () { @@ -98,6 +99,112 @@ describe('Authentication API', function () { }); }); + describe('Setup Update', function () { + describe('Setup not complete', function () { + beforeEach(testUtils.setup('roles', 'owner:pre', 'settings', 'perms:setting', 'perms:init')); + + it('should report that setup has not been completed', function (done) { + AuthAPI.isSetup().then(function (result) { + should.exist(result); + result.setup[0].status.should.be.false; + + done(); + }).catch(done); + }); + + it('should not allow setup to be updated', function (done) { + var setupData = { + name: 'test user', + email: 'test@example.com', + password: 'areallygoodpassword', + blogTitle: 'a test blog' + }; + + AuthAPI.updateSetup({setup: [setupData]}, {}).then(function () { + done(new Error('Update was able to be run')); + }).catch(function (err) { + should.exist(err); + + err.name.should.equal('NoPermissionError'); + err.code.should.equal(403); + + done(); + }); + }); + }); + + describe('Not Owner', function () { + beforeEach(testUtils.setup('roles', 'users:roles', 'settings', 'perms:setting', 'perms:init', 'perms:user')); + + it('should report that setup has been completed', function (done) { + AuthAPI.isSetup().then(function (result) { + should.exist(result); + result.setup[0].status.should.be.true; + + done(); + }).catch(done); + }); + + it('should not allow setup to be updated', function (done) { + var setupData = { + name: 'test user', + email: 'test@example.com', + password: 'areallygoodpassword', + blogTitle: 'a test blog' + }; + + AuthAPI.updateSetup({setup: [setupData]}, context.author).then(function () { + done(new Error('Update was able to be run')); + }).catch(function (err) { + should.exist(err); + + err.name.should.equal('NoPermissionError'); + err.code.should.equal(403); + + done(); + }); + }); + }); + + describe('Owner', function () { + beforeEach(testUtils.setup('roles', 'users:roles', 'settings', 'perms:setting', 'perms:init')); + + it('should report that setup has been completed', function (done) { + AuthAPI.isSetup().then(function (result) { + should.exist(result); + result.setup[0].status.should.be.true; + + done(); + }).catch(done); + }); + + it('should allow setup to be updated', function (done) { + var setupData = { + name: 'test user', + email: 'test@example.com', + password: 'areallygoodpassword', + blogTitle: 'a test blog' + }; + + AuthAPI.updateSetup({setup: [setupData]}, context.owner).then(function (result) { + should.exist(result); + should.exist(result.users); + should.not.exist(result.meta); + result.users.should.have.length(1); + testUtils.API.checkResponse(result.users[0], 'user'); + + var newUser = result.users[0]; + + newUser.id.should.equal(1); + newUser.name.should.equal(setupData.name); + newUser.email.should.equal(setupData.email); + + done(); + }).catch(done); + }); + }); + }); + // describe('Authentication', function () { // describe('Setup not completed', function () {