diff --git a/core/server/api/authentication.js b/core/server/api/authentication.js index 0a123a54f5..b76224b9d1 100644 --- a/core/server/api/authentication.js +++ b/core/server/api/authentication.js @@ -412,7 +412,7 @@ authentication = { function processInvitation(invitation) { var data = invitation.invitation[0], inviteToken = globalUtils.decodeBase64URLsafe(data.token); - return models.Invite.findOne({token: inviteToken, status: 'sent'}, _.merge({}, {include: ['roles']}, options)) + return models.Invite.findOne({token: inviteToken, status: 'sent'}, options) .then(function (_invite) { invite = _invite; @@ -428,7 +428,7 @@ authentication = { email: data.email, name: data.name, password: data.password, - roles: invite.toJSON().roles + roles: [invite.toJSON().role_id] }, options); }) .then(function () { diff --git a/core/server/api/invites.js b/core/server/api/invites.js index c678e5b125..8196dfc20b 100644 --- a/core/server/api/invites.js +++ b/core/server/api/invites.js @@ -12,7 +12,7 @@ var _ = require('lodash'), config = require('../config'), i18n = require('../i18n'), docName = 'invites', - allowedIncludes = ['created_by', 'updated_by', 'roles'], + allowedIncludes = ['created_by', 'updated_by'], invites; invites = { @@ -169,21 +169,23 @@ invites = { } function validation(options) { - var roleId; - if (!options.data.invites[0].email) { return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.invites.emailIsRequired')})); } - if (!options.data.invites[0].roles || !options.data.invites[0].roles[0]) { + if (!options.data.invites[0].role_id) { return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.invites.roleIsRequired')})); } - roleId = parseInt(options.data.invites[0].roles[0].id || options.data.invites[0].roles[0], 10); + options.data.invites[0].role_id = parseInt(options.data.invites[0].role_id, 10); // @TODO move this logic to permissible // Make sure user is allowed to add a user with this role - return dataProvider.Role.findOne({id: roleId}).then(function (role) { + return dataProvider.Role.findOne({id: options.data.invites[0].role_id}).then(function (role) { + if (!role) { + return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.invites.roleNotFound')})); + } + if (role.get('name') === 'Owner') { return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.api.invites.notAllowedToInviteOwner')})); } diff --git a/core/server/auth/auth-strategies.js b/core/server/auth/auth-strategies.js index 5bc7762f41..1547516212 100644 --- a/core/server/auth/auth-strategies.js +++ b/core/server/auth/auth-strategies.js @@ -97,7 +97,7 @@ strategies = { email: profile.email, name: profile.email, password: utils.uid(50), - roles: invite.toJSON().roles + roles: [invite.toJSON().role_id] }, options); }) .then(function destroyInvite(_user) { diff --git a/core/server/data/schema/schema.js b/core/server/data/schema/schema.js index f95d85c704..d35aa1f559 100644 --- a/core/server/data/schema/schema.js +++ b/core/server/data/schema/schema.js @@ -220,6 +220,7 @@ module.exports = { }, invites: { id: {type: 'increments', nullable: false, primary: true}, + role_id: {type: 'integer', nullable: false, unsigned: true}, status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'pending', validations: {isIn: [['pending', 'sent']]}}, token: {type: 'string', maxlength: 191, nullable: false, unique: true}, email: {type: 'string', maxlength: 191, nullable: false, unique: true, validations: {isEmail: true}}, @@ -229,11 +230,6 @@ module.exports = { updated_at: {type: 'dateTime', nullable: true}, updated_by: {type: 'integer', nullable: true} }, - invites_roles: { - id: {type: 'increments', nullable: false, primary: true}, - role_id: {type: 'integer', nullable: false}, - invite_id: {type: 'integer', nullable: false} - }, brute: { key: {type: 'string'}, firstRequest: {type: 'bigInteger'}, diff --git a/core/server/models/invite.js b/core/server/models/invite.js index 2908522d96..dd69e4f613 100644 --- a/core/server/models/invite.js +++ b/core/server/models/invite.js @@ -2,7 +2,6 @@ var ghostBookshelf = require('./base'), globalUtils = require('../utils'), crypto = require('crypto'), _ = require('lodash'), - Promise = require('bluebird'), Invite, Invites; @@ -15,10 +14,6 @@ Invite = ghostBookshelf.Model.extend({ var attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options); delete attrs.token; return attrs; - }, - - roles: function roles() { - return this.belongsToMany('Role'); } }, { orderDefaultOptions: function orderDefaultOptions() { @@ -29,31 +24,6 @@ Invite = ghostBookshelf.Model.extend({ return options; }, - filterData: function filterData(data) { - var permittedAttributes = this.prototype.permittedAttributes(), - filteredData; - - permittedAttributes.push('roles'); - filteredData = _.pick(data, permittedAttributes); - - return filteredData; - }, - - permittedOptions: function permittedOptions(methodName) { - var options = ghostBookshelf.Model.permittedOptions(), - validOptions = { - findOne: ['withRelated'], - edit: ['withRelated'], - findPage: ['withRelated'] - }; - - if (validOptions[methodName]) { - options = options.concat(validOptions[methodName]); - } - - return options; - }, - /** * @TODO: can't use base class, because: * options.withRelated = _.union(options.withRelated, options.include); is missing @@ -74,10 +44,7 @@ Invite = ghostBookshelf.Model.extend({ add: function add(data, options) { var hash = crypto.createHash('sha256'), - text = '', - roles = data.roles, - self = this, - invite; + text = ''; options = this.filterOptions(options, 'add'); options.withRelated = _.union(options.withRelated, options.include); @@ -90,31 +57,7 @@ Invite = ghostBookshelf.Model.extend({ hash.update(data.email.toLocaleLowerCase()); text += [data.expires, data.email, hash.digest('base64')].join('|'); data.token = new Buffer(text).toString('base64'); - - delete data.roles; - - return ghostBookshelf.Model.add.call(this, data, options) - .then(function (_invite) { - invite = _invite; - - return Promise.resolve(roles) - .then(function then(roles) { - roles = _.map(roles, function mapper(role) { - if (_.isString(role)) { - return parseInt(role, 10); - } else if (_.isNumber(role)) { - return role; - } else { - return parseInt(role.id, 10); - } - }); - - return invite.roles().attach(roles, options); - }); - }) - .then(function () { - return self.findOne({id: invite.id}, options); - }); + return ghostBookshelf.Model.add.call(this, data, options); } }); diff --git a/core/server/translations/en.json b/core/server/translations/en.json index 34d48a10b7..cae9b46bcc 100644 --- a/core/server/translations/en.json +++ b/core/server/translations/en.json @@ -394,6 +394,7 @@ "inviteExpired": "Invite is expired.", "emailIsRequired": "E-Mail is required.", "roleIsRequired": "Role is required", + "roleNotFound": "Role not found", "errorSendingEmail": { "error": "Error sending email: {message}", "help": "Please check your email settings and resend the invitation." diff --git a/core/test/integration/api/api_authentication_spec.js b/core/test/integration/api/api_authentication_spec.js index 6ad51562ad..031c996e70 100644 --- a/core/test/integration/api/api_authentication_spec.js +++ b/core/test/integration/api/api_authentication_spec.js @@ -257,10 +257,10 @@ describe('Authentication API', function () { it('should allow an invitation to be accepted', function () { var invite; - return models.Invite.add({email: '123@meins.de', roles: [1]}, _.merge({}, {include: ['roles']}, context.internal)) + return models.Invite.add({email: '123@meins.de', role_id: testUtils.roles.ids.admin}, context.internal) .then(function (_invite) { invite = _invite; - invite.toJSON().roles.length.should.eql(1); + invite.toJSON().role_id.should.eql(testUtils.roles.ids.admin); return models.Invite.edit({status: 'sent'}, _.merge({}, {id: invite.id}, context.internal)); }) @@ -287,14 +287,14 @@ describe('Authentication API', function () { }, _.merge({include: ['roles']}, context.internal)); }) .then(function (user) { - user.toJSON().roles.length.should.eql(1); + user.toJSON().roles.length.should.eql(testUtils.roles.ids.admin); }); }); it('should not allow an invitation to be accepted: expired', function () { var invite; - return models.Invite.add({email: '123@meins.de'}, context.internal) + return models.Invite.add({email: '123@meins.de', role_id: testUtils.roles.ids.author}, context.internal) .then(function (_invite) { invite = _invite; diff --git a/core/test/integration/api/api_invites_spec.js b/core/test/integration/api/api_invites_spec.js index b5295c9d69..ed29efdde7 100644 --- a/core/test/integration/api/api_invites_spec.js +++ b/core/test/integration/api/api_invites_spec.js @@ -29,30 +29,28 @@ describe('Invites API', function () { describe('Add', function () { it('add invite 1', function (done) { InvitesAPI.add({ - invites: [{email: 'test@example.com', roles: [testUtils.roles.ids.editor]}] - }, _.merge({}, {include: ['roles']}, testUtils.context.owner)) + invites: [{email: 'test@example.com', role_id: testUtils.roles.ids.editor}] + }, testUtils.context.owner) .then(function (response) { response.invites.length.should.eql(1); - response.invites[0].roles.length.should.eql(1); - response.invites[0].roles[0].name.should.eql('Editor'); + response.invites[0].role_id.should.eql(testUtils.roles.ids.editor); done(); }).catch(done); }); it('add invite 2', function (done) { InvitesAPI.add({ - invites: [{email: 'test2@example.com', roles: [testUtils.roles.ids.author]}] - }, _.merge({}, {include: ['roles']}, testUtils.context.owner)) + invites: [{email: 'test2@example.com', role_id: testUtils.roles.ids.author}] + }, testUtils.context.owner) .then(function (response) { response.invites.length.should.eql(1); - response.invites[0].roles.length.should.eql(1); - response.invites[0].roles[0].name.should.eql('Author'); + response.invites[0].role_id.should.eql(testUtils.roles.ids.author); done(); }).catch(done); }); it('add invite: empty invites object', function (done) { - InvitesAPI.add({invites: []}, _.merge({}, {include: ['roles']}, testUtils.context.owner)) + InvitesAPI.add({invites: []}, testUtils.context.owner) .then(function () { throw new Error('expected validation error'); }) @@ -63,7 +61,7 @@ describe('Invites API', function () { }); it('add invite: no email provided', function (done) { - InvitesAPI.add({invites: [{status: 'sent'}]}, _.merge({}, {include: ['roles']}, testUtils.context.owner)) + InvitesAPI.add({invites: [{status: 'sent'}]}, testUtils.context.owner) .then(function () { throw new Error('expected validation error'); }) @@ -76,19 +74,17 @@ describe('Invites API', function () { describe('Browse', function () { it('browse invites', function (done) { - InvitesAPI.browse(_.merge({}, {include: ['roles']}, testUtils.context.owner)) + InvitesAPI.browse(testUtils.context.owner) .then(function (response) { response.invites.length.should.eql(2); response.invites[0].status.should.eql('sent'); response.invites[0].email.should.eql('test1@ghost.org'); - response.invites[0].roles.length.should.eql(1); - response.invites[0].roles[0].name.should.eql('Administrator'); + response.invites[0].role_id.should.eql(testUtils.roles.ids.admin); response.invites[1].status.should.eql('sent'); response.invites[1].email.should.eql('test2@ghost.org'); - response.invites[1].roles.length.should.eql(1); - response.invites[1].roles[0].name.should.eql('Author'); + response.invites[1].role_id.should.eql(testUtils.roles.ids.author); should.not.exist(response.invites[0].token); should.exist(response.invites[0].expires); @@ -104,8 +100,7 @@ describe('Invites API', function () { describe('Read', function () { it('read invites: not found', function (done) { InvitesAPI.read(_.merge({}, testUtils.context.owner, { - email: 'not-existend@hey.org', - include: ['roles'] + email: 'not-existend@hey.org' })).then(function () { throw new Error('expected not found error for invite'); }).catch(function (err) { @@ -115,21 +110,19 @@ describe('Invites API', function () { }); it('read invite', function (done) { - InvitesAPI.read(_.merge({}, {email: 'test1@ghost.org', include: ['roles']}, testUtils.context.owner)) + InvitesAPI.read(_.merge({}, {email: 'test1@ghost.org'}, testUtils.context.owner)) .then(function (response) { response.invites.length.should.eql(1); - response.invites[0].roles.length.should.eql(1); - response.invites[0].roles[0].name.should.eql('Administrator'); + response.invites[0].role_id.should.eql(testUtils.roles.ids.admin); done(); }).catch(done); }); it('read invite', function (done) { - InvitesAPI.read(_.merge({}, testUtils.context.owner, {email: 'test2@ghost.org', include: ['roles']})) + InvitesAPI.read(_.merge({}, testUtils.context.owner, {email: 'test2@ghost.org'})) .then(function (response) { response.invites.length.should.eql(1); - response.invites[0].roles.length.should.eql(1); - response.invites[0].roles[0].name.should.eql('Author'); + response.invites[0].role_id.should.eql(testUtils.roles.ids.author); done(); }).catch(done); }); @@ -137,11 +130,10 @@ describe('Invites API', function () { describe('Destroy', function () { it('destroy invite', function (done) { - InvitesAPI.destroy(_.merge({}, testUtils.context.owner, {id: 1, include: ['roles']})) + InvitesAPI.destroy(_.merge({}, testUtils.context.owner, {id: 1})) .then(function () { return InvitesAPI.read(_.merge({}, testUtils.context.owner, { - email: 'test1@ghost.org', - include: ['roles'] + email: 'test1@ghost.org' })).catch(function (err) { (err instanceof errors.NotFoundError).should.eql(true); done(); @@ -180,7 +172,7 @@ describe('Invites API', function () { should.not.exist(response.meta); response.invites.should.have.length(1); - testUtils.API.checkResponse(response.invites[0], 'invites', ['roles']); + testUtils.API.checkResponse(response.invites[0], 'invites'); response.invites[0].created_at.should.be.an.instanceof(Date); } @@ -190,7 +182,7 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.owner] + role_id: testUtils.roles.ids.owner } ] }, context.owner).then(function () { @@ -203,12 +195,12 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.admin] + role_id: testUtils.roles.ids.admin } ] - }, _.merge({}, {include: ['roles']}, testUtils.context.owner)).then(function (response) { + }, testUtils.context.owner).then(function (response) { checkAddResponse(response); - response.invites[0].roles[0].name.should.equal('Administrator'); + response.invites[0].role_id.should.equal(testUtils.roles.ids.admin); done(); }).catch(done); }); @@ -218,12 +210,12 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.editor] + role_id: testUtils.roles.ids.editor } ] - }, _.merge({}, {include: ['roles']}, testUtils.context.owner)).then(function (response) { + }, testUtils.context.owner).then(function (response) { checkAddResponse(response); - response.invites[0].roles[0].name.should.equal('Editor'); + response.invites[0].role_id.should.equal(testUtils.roles.ids.editor); done(); }).catch(done); }); @@ -233,12 +225,12 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.author] + role_id: testUtils.roles.ids.author } ] - }, _.merge({}, {include: ['roles']}, testUtils.context.owner)).then(function (response) { + }, testUtils.context.owner).then(function (response) { checkAddResponse(response); - response.invites[0].roles[0].name.should.equal('Author'); + response.invites[0].role_id.should.equal(testUtils.roles.ids.author); done(); }).catch(done); }); @@ -248,12 +240,12 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.author.toString()] + role_id: testUtils.roles.ids.author.toString() } ] - }, _.merge({}, {include: ['roles']}, testUtils.context.owner)).then(function (response) { + }, testUtils.context.owner).then(function (response) { checkAddResponse(response); - response.invites[0].roles[0].name.should.equal('Author'); + response.invites[0].role_id.should.equal(testUtils.roles.ids.author); done(); }).catch(done); }); @@ -265,10 +257,10 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.owner] + role_id: testUtils.roles.ids.owner } ] - }, _.merge({}, {include: ['roles']}, testUtils.context.admin)).then(function () { + }, testUtils.context.admin).then(function () { done(new Error('Admin should not be able to add an owner')); }).catch(checkForErrorType('NoPermissionError', done)); }); @@ -278,12 +270,12 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.admin] + role_id: testUtils.roles.ids.admin } ] }, _.merge({}, {include: ['roles']}, testUtils.context.admin)).then(function (response) { checkAddResponse(response); - response.invites[0].roles[0].name.should.equal('Administrator'); + response.invites[0].role_id.should.equal(testUtils.roles.ids.admin); done(); }).catch(done); }); @@ -293,12 +285,12 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.editor] + role_id: testUtils.roles.ids.editor } ] - }, _.merge({}, {include: ['roles']}, testUtils.context.admin)).then(function (response) { + }, testUtils.context.admin).then(function (response) { checkAddResponse(response); - response.invites[0].roles[0].name.should.equal('Editor'); + response.invites[0].role_id.should.equal(testUtils.roles.ids.editor); done(); }).catch(done); }); @@ -308,12 +300,12 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.author] + role_id: testUtils.roles.ids.author } ] - }, _.merge({}, {include: ['roles']}, testUtils.context.admin)).then(function (response) { + }, testUtils.context.admin).then(function (response) { checkAddResponse(response); - response.invites[0].roles[0].name.should.equal('Author'); + response.invites[0].role_id.should.equal(testUtils.roles.ids.author); done(); }).catch(done); }); @@ -325,7 +317,7 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.owner] + role_id: testUtils.roles.ids.owner } ] }, context.editor).then(function () { @@ -338,7 +330,7 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.author] + role_id: testUtils.roles.ids.author } ] }, context.editor).then(function () { @@ -353,7 +345,7 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.owner] + role_id: testUtils.roles.ids.owner } ] }, context.author).then(function () { @@ -366,7 +358,7 @@ describe('Invites API', function () { invites: [ { email: 'test@example.com', - roles: [testUtils.roles.ids.author] + role_id: testUtils.roles.ids.author } ] }, context.author).then(function () { diff --git a/core/test/integration/model/model_invite_spec.js b/core/test/integration/model/model_invite_spec.js index 221a669ccb..3a8b200677 100644 --- a/core/test/integration/model/model_invite_spec.js +++ b/core/test/integration/model/model_invite_spec.js @@ -11,13 +11,15 @@ describe('Invite Model', function () { it('create invite', function (done) { models.Invite.add({ - email: 'test@test.de' + email: 'test@test.de', + role_id: testUtils.roles.ids.admin }, testUtils.context.internal) .then(function (invite) { should.exist(invite); should.exist(invite.get('token')); should.exist(invite.get('expires')); should.exist(invite.get('email')); + should.exist(invite.get('role_id')); done(); }) .catch(done); diff --git a/core/test/utils/fixtures/data-generator.js b/core/test/utils/fixtures/data-generator.js index 4bdeb8e31d..86f87b83d9 100644 --- a/core/test/utils/fixtures/data-generator.js +++ b/core/test/utils/fixtures/data-generator.js @@ -265,8 +265,7 @@ DataGenerator.forKnex = (function () { users, roles_users, clients, - invites, - invites_roles; + invites; function createBasic(overrides) { var newObj = _.cloneDeep(overrides); @@ -403,6 +402,7 @@ DataGenerator.forKnex = (function () { return _.defaults(newObj, { token: uuid.v4(), email: 'test@ghost.org', + role_id: 1, expires: Date.now() + (60 * 1000), created_by: 1, created_at: new Date(), @@ -477,13 +477,8 @@ DataGenerator.forKnex = (function () { ]; invites = [ - createInvite({email: 'test1@ghost.org'}), - createInvite({email: 'test2@ghost.org'}) - ]; - - invites_roles = [ - {invite_id: 1, role_id: 1}, - {invite_id: 2, role_id: 3} + createInvite({email: 'test1@ghost.org', role_id: 1}), + createInvite({email: 'test2@ghost.org', role_id: 3}) ]; return { @@ -505,7 +500,6 @@ DataGenerator.forKnex = (function () { createInvite: createInvite, invites: invites, - invites_roles: invites_roles, posts: posts, tags: tags, posts_tags: posts_tags, diff --git a/core/test/utils/index.js b/core/test/utils/index.js index 7d82831855..446577bc80 100644 --- a/core/test/utils/index.js +++ b/core/test/utils/index.js @@ -392,10 +392,7 @@ fixtures = { }, insertInvites: function insertInvites() { - return db.knex('invites').insert(DataGenerator.forKnex.invites) - .then(function () { - return db.knex('invites_roles').insert(DataGenerator.forKnex.invites_roles); - }); + return db.knex('invites').insert(DataGenerator.forKnex.invites); } };