mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Contributor Role (#9315)
closes #9314 * added fixtures for contributor role * update post api tests to prevent contributor publishing post * update permissible function in role/user model * fix additional author code in invites * update contributor role migration for knex-migrator v3 * fix paths in contrib migration * ensure contributors can't edit or delete published posts, fix routing tests [ci skip] * update db fixtures hash * strip tags from post if contributor * cleanup post permissible function * excludedAttrs to ignore tag updates for now (might be removed later) * ensure contributors can't edit another's post * migration script for 1.21
This commit is contained in:
parent
a274d61a8c
commit
777247cbc7
28 changed files with 1448 additions and 227 deletions
|
@ -192,9 +192,9 @@ invites = {
|
|||
allowed = [];
|
||||
|
||||
if (loggedInUserRole === 'Owner' || loggedInUserRole === 'Administrator') {
|
||||
allowed = ['Administrator', 'Editor', 'Author'];
|
||||
allowed = ['Administrator', 'Editor', 'Author', 'Contributor'];
|
||||
} else if (loggedInUserRole === 'Editor') {
|
||||
allowed = ['Author'];
|
||||
allowed = ['Author', 'Contributor'];
|
||||
}
|
||||
|
||||
if (allowed.indexOf(roleToInvite.get('name')) === -1) {
|
||||
|
|
|
@ -10,7 +10,7 @@ var Promise = require('bluebird'),
|
|||
allowedIncludes = [
|
||||
'created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields'
|
||||
],
|
||||
unsafeAttrs = ['author_id'],
|
||||
unsafeAttrs = ['author_id', 'status'],
|
||||
posts;
|
||||
|
||||
/**
|
||||
|
|
|
@ -209,7 +209,21 @@ utils = {
|
|||
var unsafeAttrObject = unsafeAttrNames && _.has(options, 'data.[' + docName + '][0]') ? _.pick(options.data[docName][0], unsafeAttrNames) : {},
|
||||
permsPromise = permissions.canThis(options.context)[method][singular](options.id, unsafeAttrObject);
|
||||
|
||||
return permsPromise.then(function permissionGranted() {
|
||||
return permsPromise.then(function permissionGranted(result) {
|
||||
/*
|
||||
* Allow the permissions function to return a list of excluded attributes.
|
||||
* If it does, omit those attrs from the data passed through
|
||||
*
|
||||
* NOTE: excludedAttrs differ from unsafeAttrs in that they're determined by the model's permissible function,
|
||||
* and the attributes are simply excluded rather than throwing a NoPermission exception
|
||||
*
|
||||
* TODO: This is currently only needed because of the posts model and the contributor role. Once we extend the
|
||||
* contributor role to be able to edit existing tags, this concept can be removed.
|
||||
*/
|
||||
if (result && result.excludedAttrs && _.has(options, 'data.[' + docName + '][0]')) {
|
||||
options.data[docName][0] = _.omit(options.data[docName][0], result.excludedAttrs);
|
||||
}
|
||||
|
||||
return options;
|
||||
}).catch(function handleNoPermissionError(err) {
|
||||
if (err instanceof common.errors.NoPermissionError) {
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
'use strict';
|
||||
|
||||
const merge = require('lodash/merge'),
|
||||
utils = require('../../../schema/fixtures/utils'),
|
||||
models = require('../../../../models'),
|
||||
permissions = require('../../../../services/permissions'),
|
||||
logging = require('../../../../lib/common/logging'),
|
||||
_private = {};
|
||||
|
||||
_private.addRole = function addRole(options) {
|
||||
const contributorRole = utils.findModelFixtureEntry('Role', {name: 'Contributor'}),
|
||||
message = 'Adding "Contributor" role to roles table';
|
||||
|
||||
return models.Role.findOne({name: contributorRole.name}, options)
|
||||
.then((role) => {
|
||||
if (!role) {
|
||||
logging.info(message);
|
||||
return utils.addFixturesForModel({name: 'Role', entries: [contributorRole]}, options);
|
||||
}
|
||||
|
||||
logging.warn(message);
|
||||
return Promise.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
_private.addContributorPermissions = function getPermissions(options) {
|
||||
const relations = utils.findRelationFixture('Role', 'Permission'),
|
||||
message = 'Adding permissions_roles fixtures for the contributor role';
|
||||
|
||||
return utils.addFixturesForRelation({
|
||||
from: relations.from,
|
||||
to: relations.to,
|
||||
entries: {
|
||||
Contributor: relations.entries.Contributor
|
||||
}
|
||||
}, options).then((result) => {
|
||||
if (result.done === result.expected) {
|
||||
logging.info(message);
|
||||
return;
|
||||
}
|
||||
|
||||
logging.warn(`(${result.done}/${result.expected}) ${message}`);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.config = {
|
||||
transaction: true
|
||||
};
|
||||
|
||||
module.exports.up = function addContributorRole(options) {
|
||||
var localOptions = merge({
|
||||
context: {internal: true}
|
||||
}, options);
|
||||
|
||||
return _private.addRole(localOptions).then(() => {
|
||||
return _private.addContributorPermissions(localOptions);
|
||||
}).then(() => {
|
||||
return permissions.init(localOptions);
|
||||
});
|
||||
};
|
|
@ -158,6 +158,10 @@
|
|||
"name": "Author",
|
||||
"description": "Authors"
|
||||
},
|
||||
{
|
||||
"name": "Contributor",
|
||||
"description": "Contributors"
|
||||
},
|
||||
{
|
||||
"name": "Owner",
|
||||
"description": "Blog Owner"
|
||||
|
@ -507,6 +511,17 @@
|
|||
"client": "all",
|
||||
"subscriber": ["add"],
|
||||
"theme": ["browse"]
|
||||
},
|
||||
"Contributor": {
|
||||
"post": ["browse", "read", "add"],
|
||||
"setting": ["browse", "read"],
|
||||
"slug": "all",
|
||||
"tag": ["browse", "read"],
|
||||
"user": ["browse", "read"],
|
||||
"role": ["browse"],
|
||||
"client": "all",
|
||||
"subscriber": ["add"],
|
||||
"theme": ["browse"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -264,6 +264,7 @@ module.exports = {
|
|||
addFixturesForRelation: addFixturesForRelation,
|
||||
findModelFixtureEntry: findModelFixtureEntry,
|
||||
findModelFixtures: findModelFixtures,
|
||||
findRelationFixture: findRelationFixture,
|
||||
findPermissionRelationsForObject: findPermissionRelationsForObject,
|
||||
removeFixturesForModel: removeFixturesForModel
|
||||
};
|
||||
|
|
|
@ -655,7 +655,8 @@ Post = ghostBookshelf.Model.extend({
|
|||
permissible: function permissible(postModelOrId, action, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasAppPermission) {
|
||||
var self = this,
|
||||
postModel = postModelOrId,
|
||||
origArgs;
|
||||
result = {},
|
||||
origArgs, isContributor, isAuthor, isEdit, isAdd, isDestroy;
|
||||
|
||||
// If we passed in an id instead of a model, get the model
|
||||
// then check the permissions
|
||||
|
@ -676,24 +677,58 @@ Post = ghostBookshelf.Model.extend({
|
|||
return unsafeAttrs[attr] && unsafeAttrs[attr] !== postModel.get(attr);
|
||||
}
|
||||
|
||||
function actorIsAuthor(loadedPermissions) {
|
||||
return loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Author'});
|
||||
}
|
||||
|
||||
function isOwner() {
|
||||
return unsafeAttrs.author_id && unsafeAttrs.author_id === context.user;
|
||||
}
|
||||
|
||||
if (actorIsAuthor(loadedPermissions) && action === 'edit' && isChanging('author_id')) {
|
||||
hasUserPermission = false;
|
||||
} else if (actorIsAuthor(loadedPermissions) && action === 'add') {
|
||||
function isCurrentOwner() {
|
||||
return context.user === postModel.get('author_id');
|
||||
}
|
||||
|
||||
function isPublished() {
|
||||
return unsafeAttrs.status && unsafeAttrs.status !== 'draft';
|
||||
}
|
||||
|
||||
function isDraft() {
|
||||
return postModel.get('status') === 'draft';
|
||||
}
|
||||
|
||||
isContributor = loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Contributor'});
|
||||
isAuthor = loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Author'});
|
||||
isEdit = (action === 'edit');
|
||||
isAdd = (action === 'add');
|
||||
isDestroy = (action === 'destroy');
|
||||
|
||||
if (isContributor && isEdit) {
|
||||
// Only allow contributor edit if neither status or author id are changing, and the post is a draft post
|
||||
hasUserPermission = !isChanging('status') && !isChanging('author_id') && isDraft() && isCurrentOwner();
|
||||
} else if (isContributor && isAdd) {
|
||||
// If adding, make sure it's a draft post and has the correct ownership
|
||||
hasUserPermission = !isPublished() && isOwner();
|
||||
} else if (isContributor && isDestroy) {
|
||||
// If destroying, only allow contributor to destroy their own draft posts
|
||||
hasUserPermission = isCurrentOwner() && isDraft();
|
||||
} else if (isAuthor && isEdit) {
|
||||
// Don't allow author to change author ids
|
||||
hasUserPermission = isCurrentOwner() && !isChanging('author_id');
|
||||
} else if (isAuthor && isAdd) {
|
||||
// Make sure new post is authored by the current user
|
||||
hasUserPermission = isOwner();
|
||||
} else if (postModel) {
|
||||
hasUserPermission = hasUserPermission || context.user === postModel.get('author_id');
|
||||
hasUserPermission = hasUserPermission || isCurrentOwner();
|
||||
}
|
||||
|
||||
if (isContributor) {
|
||||
// Note: at the moment primary_tag is a computed field,
|
||||
// meaning we don't add it to this list. However, if the primary_tag
|
||||
// ever becomes a db field rather than a computed field, add it to this list
|
||||
//
|
||||
// TODO: once contribitors are able to edit existing tags, this can be removed
|
||||
result.excludedAttrs = ['tags'];
|
||||
}
|
||||
|
||||
if (hasUserPermission && hasAppPermission) {
|
||||
return Promise.resolve();
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
return Promise.reject(new common.errors.NoPermissionError({message: common.i18n.t('errors.models.post.notEnoughPermission')}));
|
||||
|
|
|
@ -62,11 +62,11 @@ Role = ghostBookshelf.Model.extend({
|
|||
|
||||
if (action === 'assign' && loadedPermissions.user) {
|
||||
if (_.some(loadedPermissions.user.roles, {name: 'Owner'})) {
|
||||
checkAgainst = ['Owner', 'Administrator', 'Editor', 'Author'];
|
||||
checkAgainst = ['Owner', 'Administrator', 'Editor', 'Author', 'Contributor'];
|
||||
} else if (_.some(loadedPermissions.user.roles, {name: 'Administrator'})) {
|
||||
checkAgainst = ['Administrator', 'Editor', 'Author'];
|
||||
checkAgainst = ['Administrator', 'Editor', 'Author', 'Contributor'];
|
||||
} else if (_.some(loadedPermissions.user.roles, {name: 'Editor'})) {
|
||||
checkAgainst = ['Author'];
|
||||
checkAgainst = ['Author', 'Contributor'];
|
||||
}
|
||||
|
||||
// Role in the list of permissible roles
|
||||
|
|
|
@ -641,39 +641,31 @@ User = ghostBookshelf.Model.extend({
|
|||
}
|
||||
|
||||
if (action === 'edit') {
|
||||
// Owner can only be edited by owner
|
||||
if (loadedPermissions.user && userModel.hasRole('Owner')) {
|
||||
hasUserPermission = _.some(loadedPermissions.user.roles, {name: 'Owner'});
|
||||
}
|
||||
// Users with the role 'Editor' and 'Author' have complex permissions when the action === 'edit'
|
||||
// Users with the role 'Editor', 'Author', and 'Contributor' have complex permissions when the action === 'edit'
|
||||
// We now have all the info we need to construct the permissions
|
||||
if (loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Author'})) {
|
||||
// If this is the same user that requests the operation allow it.
|
||||
hasUserPermission = hasUserPermission || context.user === userModel.get('id');
|
||||
}
|
||||
|
||||
if (loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Editor'})) {
|
||||
if (context.user === userModel.get('id')) {
|
||||
// If this is the same user that requests the operation allow it.
|
||||
hasUserPermission = context.user === userModel.get('id');
|
||||
|
||||
// Alternatively, if the user we are trying to edit is an Author, allow it
|
||||
hasUserPermission = hasUserPermission || userModel.hasRole('Author');
|
||||
hasUserPermission = true;
|
||||
} else if (loadedPermissions.user && userModel.hasRole('Owner')) {
|
||||
// Owner can only be edited by owner
|
||||
hasUserPermission = loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Owner'});
|
||||
} else if (loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Editor'})) {
|
||||
// If the user we are trying to edit is an Author or Contributor, allow it
|
||||
hasUserPermission = userModel.hasRole('Author') || userModel.hasRole('Contributor');
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'destroy') {
|
||||
// Owner cannot be deleted EVER
|
||||
if (loadedPermissions.user && userModel.hasRole('Owner')) {
|
||||
if (userModel.hasRole('Owner')) {
|
||||
return Promise.reject(new common.errors.NoPermissionError({message: common.i18n.t('errors.models.user.notEnoughPermission')}));
|
||||
}
|
||||
|
||||
// Users with the role 'Editor' have complex permissions when the action === 'destroy'
|
||||
if (loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Editor'})) {
|
||||
// If this is the same user that requests the operation allow it.
|
||||
hasUserPermission = context.user === userModel.get('id');
|
||||
|
||||
// Alternatively, if the user we are trying to edit is an Author, allow it
|
||||
hasUserPermission = hasUserPermission || userModel.hasRole('Author');
|
||||
hasUserPermission = context.user === userModel.get('id') || userModel.hasRole('Author') || userModel.hasRole('Contributor');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1424,30 +1424,6 @@ describe('Post API', function () {
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('CANNOT add post with other author ID', function (done) {
|
||||
var post = {
|
||||
title: 'Freshly added',
|
||||
slug: 'freshly-added',
|
||||
author_id: 1
|
||||
};
|
||||
|
||||
request.post(testUtils.API.getApiQuery('posts/'))
|
||||
.set('Authorization', 'Bearer ' + authorAccessToken)
|
||||
.send({posts: [post]})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(403)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.exist(res.body.errors);
|
||||
res.body.errors[0].errorType.should.eql('NoPermissionError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edit', function () {
|
||||
|
@ -1507,10 +1483,110 @@ describe('Post API', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('CANNOT change author of own post', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/' + authorPostId + '/?include=tags'))
|
||||
.set('Authorization', 'Bearer ' + authorAccessToken)
|
||||
describe('Delete', function () {
|
||||
it('can delete own published post', function (done) {
|
||||
testUtils.createPost({
|
||||
post: {
|
||||
title: 'Author\'s test post for deletion',
|
||||
slug: 'author-delete-post-published',
|
||||
author_id: author.id,
|
||||
status: 'published'
|
||||
}
|
||||
}).then(function (post) {
|
||||
request.del(testUtils.API.getApiQuery('posts/' + post.id + '/'))
|
||||
.set('Authorization', 'Bearer ' + authorAccessToken)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(204)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
res.body.should.be.empty();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('As Contributor', function () {
|
||||
var contributorAccessToken, contributor;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
|
||||
// create contributor
|
||||
return testUtils.createUser({
|
||||
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+3@ghost.org'}),
|
||||
role: testUtils.DataGenerator.Content.roles[4]
|
||||
});
|
||||
})
|
||||
.then(function (_contributor) {
|
||||
request.user = contributor = _contributor;
|
||||
return testUtils.doAuth(request, 'posts');
|
||||
})
|
||||
.then(function (token) {
|
||||
contributorAccessToken = token;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Add', function () {
|
||||
it('can add own post', function (done) {
|
||||
var post = {
|
||||
title: 'Freshly added',
|
||||
slug: 'freshly-added',
|
||||
author_id: contributor.id
|
||||
};
|
||||
|
||||
request.post(testUtils.API.getApiQuery('posts/'))
|
||||
.set('Authorization', 'Bearer ' + contributorAccessToken)
|
||||
.send({posts: [post]})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(201)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var postBody = res.body;
|
||||
|
||||
res.headers['x-cache-invalidate'].should.eql('/p/' + postBody.posts[0].uuid + '/');
|
||||
should.exist(postBody);
|
||||
|
||||
testUtils.API.checkResponse(postBody.posts[0], 'post');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edit', function () {
|
||||
var contributorPostId;
|
||||
|
||||
before(function () {
|
||||
return testUtils.createPost({
|
||||
post: {
|
||||
title: 'Contributor\'s test post',
|
||||
slug: 'contributor-post',
|
||||
author_id: contributor.id,
|
||||
status: 'draft'
|
||||
}
|
||||
})
|
||||
.then(function (post) {
|
||||
contributorPostId = post.id;
|
||||
});
|
||||
});
|
||||
|
||||
it('can edit own post', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/' + contributorPostId + '/?include=tags&status=draft'))
|
||||
.set('Authorization', 'Bearer ' + contributorAccessToken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
|
@ -1521,33 +1597,38 @@ describe('Post API', function () {
|
|||
|
||||
var jsonResponse = res.body,
|
||||
changedTitle = 'My new Title',
|
||||
changedAuthor = ObjectId.generate();
|
||||
changedSlug = 'my-new-slug';
|
||||
|
||||
should.exist(jsonResponse.posts[0]);
|
||||
jsonResponse.posts[0].title = changedTitle;
|
||||
jsonResponse.posts[0].author = changedAuthor;
|
||||
jsonResponse.posts[0].slug = changedSlug;
|
||||
|
||||
request.put(testUtils.API.getApiQuery('posts/' + authorPostId + '/'))
|
||||
.set('Authorization', 'Bearer ' + authorAccessToken)
|
||||
request.put(testUtils.API.getApiQuery('posts/' + contributorPostId + '/'))
|
||||
.set('Authorization', 'Bearer ' + contributorAccessToken)
|
||||
.send(jsonResponse)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(403)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.exist(res.body.errors);
|
||||
res.body.errors[0].errorType.should.eql('NoPermissionError');
|
||||
var putBody = res.body;
|
||||
should.exist(putBody);
|
||||
res.headers['x-cache-invalidate'].should.eql('/p/' + putBody.posts[0].uuid + '/');
|
||||
putBody.posts[0].title.should.eql(changedTitle);
|
||||
putBody.posts[0].slug.should.eql(changedSlug);
|
||||
|
||||
testUtils.API.checkResponse(putBody.posts[0], 'post');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('CANNOT become author of other post', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?include=tags'))
|
||||
.set('Authorization', 'Bearer ' + authorAccessToken)
|
||||
it('CANNOT publish a post', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/' + contributorPostId + '/?include=tags&status=draft'))
|
||||
.set('Authorization', 'Bearer ' + contributorAccessToken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
|
@ -1557,15 +1638,13 @@ describe('Post API', function () {
|
|||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
changedTitle = 'My new Title',
|
||||
changedAuthor = author.id;
|
||||
changedStatus = 'published';
|
||||
|
||||
should.exist(jsonResponse.posts[0]);
|
||||
jsonResponse.posts[0].title = changedTitle;
|
||||
jsonResponse.posts[0].author = changedAuthor;
|
||||
jsonResponse.posts[0].status = changedStatus;
|
||||
|
||||
request.put(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
|
||||
.set('Authorization', 'Bearer ' + authorAccessToken)
|
||||
request.put(testUtils.API.getApiQuery('posts/' + contributorPostId + '/'))
|
||||
.set('Authorization', 'Bearer ' + contributorAccessToken)
|
||||
.send(jsonResponse)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
|
@ -1582,5 +1661,57 @@ describe('Post API', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Delete', function () {
|
||||
it('can delete own draft post', function (done) {
|
||||
testUtils.createPost({
|
||||
post: {
|
||||
title: 'Contrbutor\'s test post for deletion',
|
||||
slug: 'contributor-delete-post',
|
||||
author_id: contributor.id,
|
||||
status: 'draft'
|
||||
}
|
||||
}).then(function (post) {
|
||||
request.del(testUtils.API.getApiQuery('posts/' + post.id + '/'))
|
||||
.set('Authorization', 'Bearer ' + contributorAccessToken)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(204)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
res.body.should.be.empty();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t delete own published post', function (done) {
|
||||
testUtils.createPost({
|
||||
post: {
|
||||
title: 'Contributor\'s test post for deletion',
|
||||
slug: 'contributor-delete-post-published',
|
||||
author_id: contributor.id,
|
||||
status: 'published'
|
||||
}
|
||||
}).then(function (post) {
|
||||
request.del(testUtils.API.getApiQuery('posts/' + post.id + '/'))
|
||||
.set('Authorization', 'Bearer ' + contributorAccessToken)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(403)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.exist(res.body.errors);
|
||||
res.body.errors[0].errorType.should.eql('NoPermissionError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -53,7 +53,7 @@ describe('DB API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('delete all content is denied (editor, author & without authentication)', function () {
|
||||
it('delete all content is denied (editor, author, contributor & without authentication)', function () {
|
||||
return dbAPI.deleteAllContent(testUtils.context.editor).then(function () {
|
||||
throw new Error('Delete all content is not denied for editor.');
|
||||
}, function (error) {
|
||||
|
@ -61,6 +61,11 @@ describe('DB API', function () {
|
|||
return dbAPI.deleteAllContent(testUtils.context.author);
|
||||
}).then(function () {
|
||||
throw new Error('Delete all content is not denied for author.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.deleteAllContent(testUtils.context.contributor);
|
||||
}).then(function () {
|
||||
throw new Error('Delete all content is not denied for contributor.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.deleteAllContent();
|
||||
|
@ -71,7 +76,7 @@ describe('DB API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('export content is denied (editor, author & without authentication)', function () {
|
||||
it('export content is denied (editor, author, contributor & without authentication)', function () {
|
||||
return dbAPI.exportContent(testUtils.context.editor).then(function () {
|
||||
throw new Error('Export content is not denied for editor.');
|
||||
}, function (error) {
|
||||
|
@ -79,6 +84,11 @@ describe('DB API', function () {
|
|||
return dbAPI.exportContent(testUtils.context.author);
|
||||
}).then(function () {
|
||||
throw new Error('Export content is not denied for author.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.exportContent(testUtils.context.contributor);
|
||||
}).then(function () {
|
||||
throw new Error('Export content is not denied for contributor.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.exportContent();
|
||||
|
@ -89,7 +99,7 @@ describe('DB API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('import content is denied (editor, author & without authentication)', function () {
|
||||
it('import content is denied (editor, author, contributor & without authentication)', function () {
|
||||
var file = {
|
||||
originalname: 'myFile.json',
|
||||
path: '/my/path/myFile.json',
|
||||
|
@ -103,6 +113,11 @@ describe('DB API', function () {
|
|||
return dbAPI.importContent(_.extend(testUtils.context.author, file));
|
||||
}).then(function () {
|
||||
throw new Error('Import content is not denied for author.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.importContent(_.extend(testUtils.context.contributor, file));
|
||||
}).then(function () {
|
||||
throw new Error('Import content is not denied for contributor.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.importContent(file);
|
||||
|
|
|
@ -51,6 +51,17 @@ describe('Invites API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('add invite 3', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [{email: 'test3@example.com', role_id: testUtils.roles.ids.contributor}]
|
||||
}, testUtils.context.owner)
|
||||
.then(function (response) {
|
||||
response.invites.length.should.eql(1);
|
||||
response.invites[0].role_id.should.eql(testUtils.roles.ids.contributor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('add invite: empty invites object', function (done) {
|
||||
InvitesAPI.add({invites: []}, testUtils.context.owner)
|
||||
.then(function () {
|
||||
|
@ -253,6 +264,21 @@ describe('Invites API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite a Contributor', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.contributor
|
||||
}
|
||||
]
|
||||
}, testUtils.context.owner).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.contributor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite with role set as string', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
|
@ -327,6 +353,21 @@ describe('Invites API', function () {
|
|||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite a Contributor', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.contributor
|
||||
}
|
||||
]
|
||||
}, testUtils.context.admin).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.contributor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Editor', function () {
|
||||
|
@ -383,33 +424,20 @@ describe('Invites API', function () {
|
|||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Author', function () {
|
||||
it('CANNOT invite an Owner', function (done) {
|
||||
it('Can invite a Contributor', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.owner
|
||||
role_id: testUtils.roles.ids.contributor
|
||||
}
|
||||
]
|
||||
}, context.author).then(function () {
|
||||
done(new Error('Author should not be able to add an owner'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT invite an Author', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.author
|
||||
}
|
||||
]
|
||||
}, context.author).then(function () {
|
||||
done(new Error('Author should not be able to add an Author'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
}, context.editor).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.contributor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,11 +15,12 @@ describe('Roles API', function () {
|
|||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
should.exist(response.roles);
|
||||
response.roles.should.have.length(4);
|
||||
response.roles.should.have.length(5);
|
||||
testUtils.API.checkResponse(response.roles[0], 'role');
|
||||
testUtils.API.checkResponse(response.roles[1], 'role');
|
||||
testUtils.API.checkResponse(response.roles[2], 'role');
|
||||
testUtils.API.checkResponse(response.roles[3], 'role');
|
||||
testUtils.API.checkResponse(response.roles[4], 'role');
|
||||
}
|
||||
|
||||
it('Owner can browse', function (done) {
|
||||
|
@ -50,6 +51,13 @@ describe('Roles API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Contributor can browse', function (done) {
|
||||
RoleAPI.browse(context.contributor).then(function (response) {
|
||||
checkBrowseResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('No-auth CANNOT browse', function (done) {
|
||||
RoleAPI.browse().then(function () {
|
||||
done(new Error('Browse roles is not denied without authentication.'));
|
||||
|
@ -64,13 +72,15 @@ describe('Roles API', function () {
|
|||
should.exist(response);
|
||||
should.exist(response.roles);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
response.roles.should.have.length(3);
|
||||
response.roles.should.have.length(4);
|
||||
testUtils.API.checkResponse(response.roles[0], 'role');
|
||||
testUtils.API.checkResponse(response.roles[1], 'role');
|
||||
testUtils.API.checkResponse(response.roles[2], 'role');
|
||||
testUtils.API.checkResponse(response.roles[3], 'role');
|
||||
response.roles[0].name.should.equal('Administrator');
|
||||
response.roles[1].name.should.equal('Editor');
|
||||
response.roles[2].name.should.equal('Author');
|
||||
response.roles[3].name.should.equal('Contributor');
|
||||
}
|
||||
|
||||
it('Owner can assign all', function (done) {
|
||||
|
@ -87,14 +97,16 @@ describe('Roles API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Editor can assign Author', function (done) {
|
||||
it('Editor can assign Author & Contributor', function (done) {
|
||||
RoleAPI.browse(_.extend({}, context.editor, {permissions: 'assign'})).then(function (response) {
|
||||
should.exist(response);
|
||||
should.exist(response.roles);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
response.roles.should.have.length(1);
|
||||
response.roles.should.have.length(2);
|
||||
testUtils.API.checkResponse(response.roles[0], 'role');
|
||||
testUtils.API.checkResponse(response.roles[1], 'role');
|
||||
response.roles[0].name.should.equal('Author');
|
||||
response.roles[1].name.should.equal('Contributor');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
@ -109,6 +121,16 @@ describe('Roles API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Contributor CANNOT assign any', function (done) {
|
||||
RoleAPI.browse(_.extend({}, context.contributor, {permissions: 'assign'})).then(function (response) {
|
||||
should.exist(response);
|
||||
should.exist(response.roles);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
response.roles.should.have.length(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('No-auth CANNOT browse', function (done) {
|
||||
RoleAPI.browse({permissions: 'assign'}).then(function () {
|
||||
done(new Error('Browse roles is not denied without authentication.'));
|
||||
|
|
|
@ -50,6 +50,17 @@ describe('Tags API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add a tag (author)', function (done) {
|
||||
TagAPI.add({tags: [newTag]}, testUtils.context.author)
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
results.tags[0].visibility.should.eql('public');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('add internal tag', function (done) {
|
||||
TagAPI
|
||||
.add({tags: [{name: '#test'}]}, testUtils.context.editor)
|
||||
|
@ -64,6 +75,15 @@ describe('Tags API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('CANNOT add tag (contributor)', function (done) {
|
||||
TagAPI.add({tags: [newTag]}, testUtils.context.contributor)
|
||||
.then(function () {
|
||||
done(new Error('Add tag is not denied for contributor.'));
|
||||
}, function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('No-auth CANNOT add tag', function (done) {
|
||||
TagAPI.add({tags: [newTag]}).then(function () {
|
||||
done(new Error('Add tag is not denied without authentication.'));
|
||||
|
@ -111,6 +131,15 @@ describe('Tags API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('CANNOT edit a tag (author)', function (done) {
|
||||
TagAPI.edit({tags: [{name: newTagName}]}, _.extend({}, context.author, {id: firstTag}))
|
||||
.then(function () {
|
||||
done(new Error('Add tag is not denied for author.'));
|
||||
}, function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('No-auth CANNOT edit tag', function (done) {
|
||||
TagAPI.edit({tags: [{name: newTagName}]}, _.extend({}, {id: firstTag}))
|
||||
.then(function () {
|
||||
|
|
|
@ -75,46 +75,54 @@ describe('Users API', function () {
|
|||
testUtils.API.checkResponse(response.users[1], 'user', additional, missing, only, options);
|
||||
testUtils.API.checkResponse(response.users[2], 'user', additional, missing, only, options);
|
||||
testUtils.API.checkResponse(response.users[3], 'user', additional, missing, only, options);
|
||||
testUtils.API.checkResponse(response.users[4], 'user', additional, missing, only, options);
|
||||
}
|
||||
|
||||
it('Owner can browse', function (done) {
|
||||
UserAPI.browse(context.owner).then(function (response) {
|
||||
checkBrowseResponse(response, 7);
|
||||
checkBrowseResponse(response, 9);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Admin can browse', function (done) {
|
||||
UserAPI.browse(context.admin).then(function (response) {
|
||||
checkBrowseResponse(response, 7);
|
||||
checkBrowseResponse(response, 9);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Editor can browse', function (done) {
|
||||
UserAPI.browse(context.editor).then(function (response) {
|
||||
checkBrowseResponse(response, 7);
|
||||
checkBrowseResponse(response, 9);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Author can browse active', function (done) {
|
||||
UserAPI.browse(context.author).then(function (response) {
|
||||
checkBrowseResponse(response, 7);
|
||||
checkBrowseResponse(response, 9);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Contributor can browse active', function (done) {
|
||||
UserAPI.browse(context.contributor).then(function (response) {
|
||||
checkBrowseResponse(response, 9);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('No-auth CAN browse, but only gets filtered active users', function (done) {
|
||||
UserAPI.browse().then(function (response) {
|
||||
checkBrowseResponse(response, 7, null, null, null, {public: true});
|
||||
checkBrowseResponse(response, 9, null, null, null, {public: true});
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can browse all', function (done) {
|
||||
UserAPI.browse(_.extend({}, testUtils.context.admin, {status: 'all'})).then(function (response) {
|
||||
checkBrowseResponse(response, 7);
|
||||
checkBrowseResponse(response, 9);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
@ -127,7 +135,7 @@ describe('Users API', function () {
|
|||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'users');
|
||||
should.exist(response.users);
|
||||
response.users.should.have.length(7);
|
||||
response.users.should.have.length(9);
|
||||
testUtils.API.checkResponse(response.users[0], 'user', 'roles');
|
||||
testUtils.API.checkResponse(response.users[1], 'user', 'roles');
|
||||
testUtils.API.checkResponse(response.users[2], 'user', 'roles');
|
||||
|
@ -187,8 +195,7 @@ describe('Users API', function () {
|
|||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'users');
|
||||
should.exist(response.users);
|
||||
response.users.should.have.length(7);
|
||||
response.users.should.have.length(7);
|
||||
response.users.should.have.length(9);
|
||||
|
||||
testUtils.API.checkResponse(response.users[0], 'user', 'count');
|
||||
testUtils.API.checkResponse(response.users[1], 'user', 'count');
|
||||
|
@ -197,19 +204,23 @@ describe('Users API', function () {
|
|||
testUtils.API.checkResponse(response.users[4], 'user', 'count');
|
||||
testUtils.API.checkResponse(response.users[5], 'user', 'count');
|
||||
testUtils.API.checkResponse(response.users[6], 'user', 'count');
|
||||
testUtils.API.checkResponse(response.users[7], 'user', 'count');
|
||||
testUtils.API.checkResponse(response.users[8], 'user', 'count');
|
||||
|
||||
response.users[0].count.posts.should.eql(0);
|
||||
response.users[1].count.posts.should.eql(0);
|
||||
response.users[2].count.posts.should.eql(0);
|
||||
response.users[3].count.posts.should.eql(8);
|
||||
response.users[3].count.posts.should.eql(0);
|
||||
response.users[4].count.posts.should.eql(0);
|
||||
response.users[5].count.posts.should.eql(0);
|
||||
response.users[5].count.posts.should.eql(8);
|
||||
response.users[6].count.posts.should.eql(0);
|
||||
response.users[7].count.posts.should.eql(0);
|
||||
response.users[8].count.posts.should.eql(0);
|
||||
|
||||
response.meta.pagination.should.have.property('page', 1);
|
||||
response.meta.pagination.should.have.property('limit', 15);
|
||||
response.meta.pagination.should.have.property('pages', 1);
|
||||
response.meta.pagination.should.have.property('total', 7);
|
||||
response.meta.pagination.should.have.property('total', 9);
|
||||
response.meta.pagination.should.have.property('next', null);
|
||||
response.meta.pagination.should.have.property('prev', null);
|
||||
|
||||
|
@ -263,6 +274,13 @@ describe('Users API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Contributor can read', function (done) {
|
||||
UserAPI.read(_.extend({}, context.contributor, {id: userIdFor.owner})).then(function (response) {
|
||||
checkReadResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('No-auth can read', function (done) {
|
||||
UserAPI.read({id: userIdFor.owner}).then(function (response) {
|
||||
checkReadResponse(response, true, null, null, null, {public: true});
|
||||
|
@ -320,11 +338,15 @@ describe('Users API', function () {
|
|||
}).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
|
||||
return UserAPI.edit({users: [{name: newName}]}, _.extend({}, context.owner, {id: userIdFor.contributor}));
|
||||
}).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Admin can edit Admin, Editor and Author roles', function (done) {
|
||||
it('Admin can edit Admin, Editor, Author, and Contributor roles', function (done) {
|
||||
UserAPI.edit({users: [{name: newName}]}, _.extend({}, context.admin, {id: userIdFor.admin}))
|
||||
.then(function (response) {
|
||||
checkEditResponse(response);
|
||||
|
@ -336,6 +358,10 @@ describe('Users API', function () {
|
|||
}).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
|
||||
return UserAPI.edit({users: [{name: newName}]}, _.extend({}, context.admin, {id: userIdFor.contributor}));
|
||||
}).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
@ -350,7 +376,7 @@ describe('Users API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('Admin can edit Admin, Editor and Author roles with roles in payload', function (done) {
|
||||
it('Admin can edit Admin, Editor, Author, and Contributor roles with roles in payload', function (done) {
|
||||
UserAPI.edit({
|
||||
users: [{
|
||||
name: newName,
|
||||
|
@ -376,54 +402,25 @@ describe('Users API', function () {
|
|||
}).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
|
||||
return UserAPI.edit({
|
||||
users: [{
|
||||
name: newName,
|
||||
roles: [roleIdFor.contributor]
|
||||
}]
|
||||
}, _.extend({}, context.admin, {id: userIdFor.contributor}));
|
||||
}).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Editor CANNOT edit Owner, Admin or Editor roles', function (done) {
|
||||
// Cannot edit Owner
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName}]}, _.extend({}, context.editor, {id: userIdFor.owner})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to edit owner account'));
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
}).finally(function () {
|
||||
// Cannot edit Admin
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName}]}, _.extend({}, context.editor, {id: userIdFor.admin})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to edit admin account'));
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
}).finally(function () {
|
||||
// Cannot edit Editor
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName}]}, _.extend({}, context.editor, {id: testUtils.DataGenerator.Content.extraUsers[1].id})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to edit other editor account'));
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Editor can edit self or Author role', function (done) {
|
||||
// Can edit self
|
||||
UserAPI.edit(
|
||||
it('Editor can edit self', function () {
|
||||
return UserAPI.edit(
|
||||
{users: [{name: newName}]}, _.extend({}, context.editor, {id: userIdFor.editor})
|
||||
).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
// Can edit Author
|
||||
return UserAPI.edit(
|
||||
{users: [{name: newName}]}, _.extend({}, context.editor, {id: userIdFor.author})
|
||||
);
|
||||
}).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Author CANNOT edit all roles', function (done) {
|
||||
|
@ -431,7 +428,7 @@ describe('Users API', function () {
|
|||
UserAPI.edit(
|
||||
{users: [{name: newName}]}, _.extend({}, context.author, {id: userIdFor.owner})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to edit owner account'));
|
||||
done(new Error('Author should not be able to edit owner account'));
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
}).finally(function () {
|
||||
|
@ -439,7 +436,7 @@ describe('Users API', function () {
|
|||
UserAPI.edit(
|
||||
{users: [{name: newName}]}, _.extend({}, context.author, {id: userIdFor.admin})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to edit admin account'));
|
||||
done(new Error('Author should not be able to edit admin account'));
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
}).finally(function () {
|
||||
|
@ -495,6 +492,16 @@ describe('Users API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Contributor can edit self', function (done) {
|
||||
// Next test that contributor CAN edit self
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName}]}, _.extend({}, context.contributor, {id: userIdFor.contributor})
|
||||
).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Does not allow password to be set', function (done) {
|
||||
UserAPI.edit(
|
||||
{
|
||||
|
@ -796,7 +803,7 @@ describe('Users API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('[failure] can change status to inactive for author', function () {
|
||||
it('[failure] can\'t change status to inactive for author', function () {
|
||||
return UserAPI.edit(
|
||||
{
|
||||
users: [
|
||||
|
@ -812,6 +819,24 @@ describe('Users API', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('as contributor', function () {
|
||||
it('[failure] can\' change my own status to inactive', function () {
|
||||
return UserAPI.edit(
|
||||
{
|
||||
users: [
|
||||
{
|
||||
status: 'inactive'
|
||||
}
|
||||
]
|
||||
}, _.extend({}, context.contributor, {id: userIdFor.contributor})
|
||||
).then(function () {
|
||||
throw new Error('this is not allowed');
|
||||
}).catch(function (err) {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -915,7 +940,7 @@ describe('Users API', function () {
|
|||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('Can destroy admin, editor, author', function (done) {
|
||||
it('Can destroy admin, editor, author, contributor', function (done) {
|
||||
// Admin
|
||||
UserAPI.destroy(_.extend({}, context.owner, {id: userIdFor.admin}))
|
||||
.then(function (response) {
|
||||
|
@ -930,6 +955,10 @@ describe('Users API', function () {
|
|||
}).then(function (response) {
|
||||
should.not.exist(response);
|
||||
|
||||
// Contributor
|
||||
return UserAPI.destroy(_.extend({}, context.owner, {id: userIdFor.contributor}));
|
||||
}).then(function (response) {
|
||||
should.not.exist(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
@ -943,15 +972,15 @@ describe('Users API', function () {
|
|||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('Can destroy admin, editor, author', function (done) {
|
||||
it('Can destroy admin, editor, author, contributor', function (done) {
|
||||
// Admin
|
||||
UserAPI.destroy(_.extend({}, context.admin, {id: testUtils.DataGenerator.Content.extraUsers[0].id}))
|
||||
.then(function (response) {
|
||||
should.not.exist(response);
|
||||
.then(function (response) {
|
||||
should.not.exist(response);
|
||||
|
||||
// Editor
|
||||
return UserAPI.destroy(_.extend({}, context.admin, {id: testUtils.DataGenerator.Content.extraUsers[1].id}));
|
||||
}).then(function (response) {
|
||||
// Editor
|
||||
return UserAPI.destroy(_.extend({}, context.admin, {id: testUtils.DataGenerator.Content.extraUsers[1].id}));
|
||||
}).then(function (response) {
|
||||
should.not.exist(response);
|
||||
|
||||
// Author
|
||||
|
@ -959,6 +988,10 @@ describe('Users API', function () {
|
|||
}).then(function (response) {
|
||||
should.not.exist(response);
|
||||
|
||||
// Contributor
|
||||
return UserAPI.destroy(_.extend({}, context.admin, {id: testUtils.DataGenerator.Content.extraUsers[3].id}));
|
||||
}).then(function (response) {
|
||||
should.not.exist(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
@ -1039,6 +1072,22 @@ describe('Users API', function () {
|
|||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Contributor', function () {
|
||||
it('CANNOT destroy owner', function (done) {
|
||||
UserAPI.destroy(_.extend({}, context.contributor, {id: userIdFor.owner}))
|
||||
.then(function () {
|
||||
done(new Error('Contributor should not be able to delete owner'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT destroy self', function (done) {
|
||||
UserAPI.destroy(_.extend({}, context.contributor, {id: userIdFor.contributor}))
|
||||
.then(function () {
|
||||
done(new Error('Contributor should not be able to delete self'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edit and assign role', function () {
|
||||
|
@ -1122,6 +1171,26 @@ describe('Users API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Can assign Contributor role', function (done) {
|
||||
var options = _.extend({}, context.owner, {id: userIdFor.admin}, {include: 'roles'});
|
||||
UserAPI.read(options).then(function (response) {
|
||||
response.users[0].id.should.equal(userIdFor.admin);
|
||||
response.users[0].roles[0].name.should.equal('Administrator');
|
||||
|
||||
return UserAPI.edit({
|
||||
users: [
|
||||
{name: newName, roles: [roleIdFor.contributor]}
|
||||
]
|
||||
}, options).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
response.users[0].id.should.equal(userIdFor.admin);
|
||||
response.users[0].roles[0].name.should.equal('Contributor');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Admin', function () {
|
||||
|
@ -1185,6 +1254,26 @@ describe('Users API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('Can assign Contributor role', function (done) {
|
||||
var options = _.extend({}, context.admin, {id: userIdFor.editor}, {include: 'roles'});
|
||||
UserAPI.read(options).then(function (response) {
|
||||
response.users[0].id.should.equal(userIdFor.editor);
|
||||
response.users[0].roles[0].name.should.equal('Editor');
|
||||
|
||||
return UserAPI.edit({
|
||||
users: [
|
||||
{name: newName, roles: [roleIdFor.contributor]}
|
||||
]
|
||||
}, options).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
response.users[0].id.should.equal(userIdFor.editor);
|
||||
response.users[0].roles[0].name.should.equal('Contributor');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('CANNOT downgrade owner', function (done) {
|
||||
var options = _.extend({}, context.admin, {id: userIdFor.owner}, {include: 'roles'});
|
||||
UserAPI.read(options).then(function (response) {
|
||||
|
@ -1214,12 +1303,47 @@ describe('Users API', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can assign contributor role to author', function (done) {
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName, roles: [roleIdFor.contributor]}]},
|
||||
_.extend({}, context.editor, {id: testUtils.DataGenerator.Content.extraUsers[2].id}, {include: 'roles'})
|
||||
).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
response.users[0].id.should.equal(testUtils.DataGenerator.Content.extraUsers[2].id);
|
||||
response.users[0].roles[0].name.should.equal('Contributor');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can assign contributor role to contributor', function (done) {
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName, roles: [roleIdFor.contributor]}]},
|
||||
_.extend({}, context.editor, {id: testUtils.DataGenerator.Content.extraUsers[3].id}, {include: 'roles'})
|
||||
).then(function (response) {
|
||||
checkEditResponse(response);
|
||||
response.users[0].id.should.equal(testUtils.DataGenerator.Content.extraUsers[3].id);
|
||||
response.users[0].roles[0].name.should.equal('Contributor');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('CANNOT assign author role to self', function (done) {
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName, roles: [roleIdFor.author]}]},
|
||||
_.extend({}, context.editor, {id: userIdFor.editor}, {include: 'roles'})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to upgrade their role'));
|
||||
done(new Error('Editor should not be able to downgrade their role'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT assign contributor role to self', function (done) {
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName, roles: [roleIdFor.contributor]}]},
|
||||
_.extend({}, context.editor, {id: userIdFor.editor}, {include: 'roles'})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to downgrade their role'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
|
@ -1232,6 +1356,15 @@ describe('Users API', function () {
|
|||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT assign contributor role to other Editor', function (done) {
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName, roles: [roleIdFor.contributor]}]},
|
||||
_.extend({}, context.editor, {id: testUtils.DataGenerator.Content.extraUsers[1].id}, {include: 'roles'})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to change the roles of other editors'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT assign author role to admin', function (done) {
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName, roles: [roleIdFor.author]}]},
|
||||
|
@ -1240,12 +1373,31 @@ describe('Users API', function () {
|
|||
done(new Error('Editor should not be able to change the roles of admins'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT assign contributor role to admin', function (done) {
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName, roles: [roleIdFor.contributor]}]},
|
||||
_.extend({}, context.editor, {id: userIdFor.admin}, {include: 'roles'})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to change the roles of admins'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT assign admin role to author', function (done) {
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName, roles: [roleIdFor.admin]}]},
|
||||
_.extend({}, context.editor, {id: userIdFor.author}, {include: 'roles'})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to upgrade the role of authors'));
|
||||
done(new Error('Editor should not be able to upgrade the role of contributors'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT assign admin role to contributor', function (done) {
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName, roles: [roleIdFor.admin]}]},
|
||||
_.extend({}, context.editor, {id: userIdFor.contributor}, {include: 'roles'})
|
||||
).then(function () {
|
||||
done(new Error('Editor should not be able to upgrade the role of contributors'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
});
|
||||
|
@ -1260,6 +1412,17 @@ describe('Users API', function () {
|
|||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Contributor', function () {
|
||||
it('CANNOT assign higher role to self', function (done) {
|
||||
UserAPI.edit(
|
||||
{users: [{name: newName, roles: [roleIdFor.editor]}]},
|
||||
_.extend({}, context.contributor, {id: userIdFor.contributor}, {include: 'roles'})
|
||||
).then(function () {
|
||||
done(new Error('Contributor should not be able to upgrade their role'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Transfer ownership', function () {
|
||||
|
@ -1305,7 +1468,7 @@ describe('Users API', function () {
|
|||
{owner: [{id: userIdFor.admin}]},
|
||||
context.editor
|
||||
).then(function () {
|
||||
done(new Error('Admin is not denied transferring ownership.'));
|
||||
done(new Error('Editor is not denied transferring ownership.'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
|
@ -1315,7 +1478,17 @@ describe('Users API', function () {
|
|||
{owner: [{id: userIdFor.admin}]},
|
||||
context.author
|
||||
).then(function () {
|
||||
done(new Error('Admin is not denied transferring ownership.'));
|
||||
done(new Error('Author is not denied transferring ownership.'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('Contributor CANNOT transfer ownership', function (done) {
|
||||
// transfer ownership to user id: 2
|
||||
UserAPI.transferOwnership(
|
||||
{owner: [{id: userIdFor.admin}]},
|
||||
context.contributor
|
||||
).then(function () {
|
||||
done(new Error('Contributor is not denied transferring ownership.'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
|
|
|
@ -139,7 +139,7 @@ describe('Webhooks API', function () {
|
|||
it('CANNOT delete', function (done) {
|
||||
WebhookAPI.destroy(_.extend({}, testUtils.context.author, {id: firstWebhook}))
|
||||
.then(function () {
|
||||
done(new Error('Editor should not be able to delete a webhook'));
|
||||
done(new Error('Author should not be able to delete a webhook'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1259,8 +1259,8 @@ describe('Import (new test structure)', function () {
|
|||
users = importedData[0];
|
||||
rolesUsers = importedData[1];
|
||||
|
||||
// we imported 3 users, there were already 3 users, only one of the imported users is new
|
||||
users.length.should.equal(6, 'There should only be 6 users');
|
||||
// we imported 3 users, there were already 4 users
|
||||
users.length.should.equal(7, 'There should only be 7 users');
|
||||
|
||||
// the original owner user is first
|
||||
ownerUser = users[0];
|
||||
|
@ -1292,7 +1292,7 @@ describe('Import (new test structure)', function () {
|
|||
newUser.updated_by.should.equal(ownerUser.id);
|
||||
newUser.updated_at.should.not.equal(exportData.data.users[1].updated_at);
|
||||
|
||||
rolesUsers.length.should.equal(6, 'There should be 6 role relations');
|
||||
rolesUsers.length.should.equal(7, 'There should be 7 role relations');
|
||||
|
||||
_.each(rolesUsers, function (roleUser) {
|
||||
if (roleUser.user_id === ownerUser.id) {
|
||||
|
@ -1450,10 +1450,10 @@ describe('Import (new test structure)', function () {
|
|||
return user.name === exportData.data.users[2].name;
|
||||
});
|
||||
|
||||
// we imported 3 users, there were already 4 users, only one of the imported users is new
|
||||
users.length.should.equal(5, 'There should only be three users');
|
||||
// we imported 3 users, there were already 5 users, only one of the imported users is new
|
||||
users.length.should.equal(6, 'There should only be three users');
|
||||
|
||||
rolesUsers.length.should.equal(5, 'There should be 5 role relations');
|
||||
rolesUsers.length.should.equal(6, 'There should be 6 role relations');
|
||||
|
||||
_.each(rolesUsers, function (roleUser) {
|
||||
if (roleUser.user_id === ownerUser.id) {
|
||||
|
|
|
@ -56,33 +56,33 @@ describe('Database Migration (special functions)', function () {
|
|||
|
||||
// Posts
|
||||
permissions[7].name.should.eql('Browse posts');
|
||||
permissions[7].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[7].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[8].name.should.eql('Read posts');
|
||||
permissions[8].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[8].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[9].name.should.eql('Edit posts');
|
||||
permissions[9].should.be.AssignedToRoles(['Administrator', 'Editor']);
|
||||
permissions[10].name.should.eql('Add posts');
|
||||
permissions[10].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[10].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[11].name.should.eql('Delete posts');
|
||||
permissions[11].should.be.AssignedToRoles(['Administrator', 'Editor']);
|
||||
|
||||
// Settings
|
||||
permissions[12].name.should.eql('Browse settings');
|
||||
permissions[12].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[12].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[13].name.should.eql('Read settings');
|
||||
permissions[13].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[13].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[14].name.should.eql('Edit settings');
|
||||
permissions[14].should.be.AssignedToRoles(['Administrator']);
|
||||
|
||||
// Slugs
|
||||
permissions[15].name.should.eql('Generate slugs');
|
||||
permissions[15].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[15].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
|
||||
// Tags
|
||||
permissions[16].name.should.eql('Browse tags');
|
||||
permissions[16].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[16].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[17].name.should.eql('Read tags');
|
||||
permissions[17].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[17].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[18].name.should.eql('Edit tags');
|
||||
permissions[18].should.be.AssignedToRoles(['Administrator', 'Editor']);
|
||||
permissions[19].name.should.eql('Add tags');
|
||||
|
@ -92,7 +92,7 @@ describe('Database Migration (special functions)', function () {
|
|||
|
||||
// Themes
|
||||
permissions[21].name.should.eql('Browse themes');
|
||||
permissions[21].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[21].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[22].name.should.eql('Edit themes');
|
||||
permissions[22].should.be.AssignedToRoles(['Administrator']);
|
||||
permissions[23].name.should.eql('Activate themes');
|
||||
|
@ -106,9 +106,9 @@ describe('Database Migration (special functions)', function () {
|
|||
|
||||
// Users
|
||||
permissions[27].name.should.eql('Browse users');
|
||||
permissions[27].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[27].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[28].name.should.eql('Read users');
|
||||
permissions[28].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[28].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[29].name.should.eql('Edit users');
|
||||
permissions[29].should.be.AssignedToRoles(['Administrator', 'Editor']);
|
||||
permissions[30].name.should.eql('Add users');
|
||||
|
@ -120,19 +120,19 @@ describe('Database Migration (special functions)', function () {
|
|||
permissions[32].name.should.eql('Assign a role');
|
||||
permissions[32].should.be.AssignedToRoles(['Administrator', 'Editor']);
|
||||
permissions[33].name.should.eql('Browse roles');
|
||||
permissions[33].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[33].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
|
||||
// Clients
|
||||
permissions[34].name.should.eql('Browse clients');
|
||||
permissions[34].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[34].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[35].name.should.eql('Read clients');
|
||||
permissions[35].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[35].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[36].name.should.eql('Edit clients');
|
||||
permissions[36].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[36].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[37].name.should.eql('Add clients');
|
||||
permissions[37].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[37].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[38].name.should.eql('Delete clients');
|
||||
permissions[38].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[38].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
|
||||
// Subscribers
|
||||
permissions[39].name.should.eql('Browse subscribers');
|
||||
|
@ -142,7 +142,7 @@ describe('Database Migration (special functions)', function () {
|
|||
permissions[41].name.should.eql('Edit subscribers');
|
||||
permissions[41].should.be.AssignedToRoles(['Administrator']);
|
||||
permissions[42].name.should.eql('Add subscribers');
|
||||
permissions[42].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author']);
|
||||
permissions[42].should.be.AssignedToRoles(['Administrator', 'Editor', 'Author', 'Contributor']);
|
||||
permissions[43].name.should.eql('Delete subscribers');
|
||||
permissions[43].should.be.AssignedToRoles(['Administrator']);
|
||||
|
||||
|
@ -217,11 +217,12 @@ describe('Database Migration (special functions)', function () {
|
|||
|
||||
// Roles
|
||||
should.exist(result.roles);
|
||||
result.roles.length.should.eql(4);
|
||||
result.roles.length.should.eql(5);
|
||||
result.roles.at(0).get('name').should.eql('Administrator');
|
||||
result.roles.at(1).get('name').should.eql('Editor');
|
||||
result.roles.at(2).get('name').should.eql('Author');
|
||||
result.roles.at(3).get('name').should.eql('Owner');
|
||||
result.roles.at(3).get('name').should.eql('Contributor');
|
||||
result.roles.at(4).get('name').should.eql('Owner');
|
||||
|
||||
// Permissions
|
||||
result.permissions.length.should.eql(53);
|
||||
|
|
|
@ -1681,12 +1681,12 @@ describe('Post Model', function () {
|
|||
found.length.should.equal(50);
|
||||
return PostModel.destroyByAuthor(authorData);
|
||||
}).then(function (results) {
|
||||
// User 1 has 13 posts in the database
|
||||
results.length.should.equal(13);
|
||||
// User 1 has 10 posts in the database
|
||||
results.length.should.equal(10);
|
||||
return PostModel.findAll({context: {internal: true}});
|
||||
}).then(function (found) {
|
||||
// Only 37 should remain
|
||||
found.length.should.equal(37);
|
||||
// Only 40 should remain
|
||||
found.length.should.equal(40);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
|
|
@ -215,7 +215,7 @@ describe('User Model', function run() {
|
|||
it('can findAll', function (done) {
|
||||
UserModel.findAll().then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.equal(4);
|
||||
results.length.should.equal(5);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
|
@ -228,7 +228,7 @@ describe('User Model', function run() {
|
|||
results.meta.pagination.page.should.equal(1);
|
||||
results.meta.pagination.limit.should.equal(15);
|
||||
results.meta.pagination.pages.should.equal(1);
|
||||
results.users.length.should.equal(4);
|
||||
results.users.length.should.equal(5);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
|
@ -274,7 +274,7 @@ describe('User Model', function run() {
|
|||
results.meta.pagination.page.should.equal(1);
|
||||
results.meta.pagination.limit.should.equal('all');
|
||||
results.meta.pagination.pages.should.equal(1);
|
||||
results.users.length.should.equal(7);
|
||||
results.users.length.should.equal(9);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -661,5 +661,29 @@ describe('API Utils', function () {
|
|||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('should strip excludedAttrs from data if permissions function returns them', function () {
|
||||
var testStub = sandbox.stub().resolves({excludedAttrs: ['foo']}),
|
||||
permsStub = sandbox.stub(permissions, 'canThis').returns({
|
||||
testing: {
|
||||
test: testStub
|
||||
}
|
||||
}),
|
||||
permsFunc = apiUtils.handlePermissions('tests', 'testing'),
|
||||
testObj = {data: {tests: [{id: 5, name: 'testing', foo: 'bar'}]}, id: 5};
|
||||
|
||||
return permsFunc(testObj).then(function (res) {
|
||||
permsStub.calledOnce.should.be.true();
|
||||
testStub.calledOnce.should.be.true();
|
||||
testStub.calledWithExactly(5, {}).should.be.true();
|
||||
|
||||
should(res).deepEqual({
|
||||
data: {
|
||||
tests: [{id: 5, name: 'testing'}]
|
||||
},
|
||||
id: 5
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -151,19 +151,19 @@ describe('Migration Fixture Utils', function () {
|
|||
fixtureUtils.addFixturesForRelation(fixtures.relations[0]).then(function (result) {
|
||||
should.exist(result);
|
||||
result.should.be.an.Object();
|
||||
result.should.have.property('expected', 34);
|
||||
result.should.have.property('done', 34);
|
||||
result.should.have.property('expected', 43);
|
||||
result.should.have.property('done', 43);
|
||||
|
||||
// Permissions & Roles
|
||||
permsAllStub.calledOnce.should.be.true();
|
||||
rolesAllStub.calledOnce.should.be.true();
|
||||
dataMethodStub.filter.callCount.should.eql(34);
|
||||
dataMethodStub.find.callCount.should.eql(3);
|
||||
baseUtilAttachStub.callCount.should.eql(34);
|
||||
dataMethodStub.filter.callCount.should.eql(43);
|
||||
dataMethodStub.find.callCount.should.eql(4);
|
||||
baseUtilAttachStub.callCount.should.eql(43);
|
||||
|
||||
fromItem.related.callCount.should.eql(34);
|
||||
fromItem.findWhere.callCount.should.eql(34);
|
||||
toItem[0].get.callCount.should.eql(68);
|
||||
fromItem.related.callCount.should.eql(43);
|
||||
fromItem.findWhere.callCount.should.eql(43);
|
||||
toItem[0].get.callCount.should.eql(86);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
|
|
|
@ -20,7 +20,7 @@ var should = require('should'), // jshint ignore:line
|
|||
describe('DB version integrity', function () {
|
||||
// Only these variables should need updating
|
||||
var currentSchemaHash = '329f9b498944c459040426e16fc65b11',
|
||||
currentFixturesHash = '90925e0004a0cedd1e6ea789c81ec67d';
|
||||
currentFixturesHash = 'bd814f2c2aa19c745fc84f9ecc615140';
|
||||
|
||||
// If this test is failing, then it is likely a change has been made that requires a DB version bump,
|
||||
// and the values above will need updating as confirmation
|
||||
|
|
475
core/test/unit/models/post_spec.js
Normal file
475
core/test/unit/models/post_spec.js
Normal file
|
@ -0,0 +1,475 @@
|
|||
var should = require('should'), // jshint ignore:line
|
||||
sinon = require('sinon'),
|
||||
models = require('../../../server/models'),
|
||||
common = require('../../../server/lib/common'),
|
||||
utils = require('../../utils'),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Models: Post', function () {
|
||||
before(function () {
|
||||
models.init();
|
||||
});
|
||||
|
||||
describe('Permissible', function () {
|
||||
describe('As Contributor', function () {
|
||||
describe('Editing', function () {
|
||||
it('rejects if changing status', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {status: 'published'};
|
||||
|
||||
mockPostObj.get.withArgs('status').returns('draft');
|
||||
mockPostObj.get.withArgs('author_id').returns(1);
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
false
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if changing author id', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {status: 'draft', author_id: 2};
|
||||
|
||||
mockPostObj.get.withArgs('status').returns('draft');
|
||||
mockPostObj.get.withArgs('author_id').returns(1);
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.calledTwice).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if post is not draft', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {status: 'published', author_id: 1};
|
||||
|
||||
mockPostObj.get.withArgs('status').returns('published');
|
||||
mockPostObj.get.withArgs('author_id').returns(1);
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.calledThrice).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if contributor is not author of post', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {status: 'draft', author_id: 2};
|
||||
|
||||
mockPostObj.get.withArgs('status').returns('draft');
|
||||
mockPostObj.get.withArgs('author_id').returns(2);
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.callCount).eql(4);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves if none of the above cases are true', function () {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {status: 'draft', author_id: 1};
|
||||
|
||||
mockPostObj.get.withArgs('status').returns('draft');
|
||||
mockPostObj.get.withArgs('author_id').returns(1);
|
||||
|
||||
return models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
true
|
||||
).then((result) => {
|
||||
should.exist(result);
|
||||
should(result.excludedAttrs).deepEqual(['tags']);
|
||||
should(mockPostObj.get.callCount).eql(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Adding', function () {
|
||||
it('rejects if "published" status', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {status: 'published', author_id: 1};
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'add',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.called).be.false();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if different author id', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {status: 'draft', author_id: 2};
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'add',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.called).be.false();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves if none of the above cases are true', function () {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {status: 'draft', author_id: 1};
|
||||
|
||||
return models.Post.permissible(
|
||||
mockPostObj,
|
||||
'add',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
true
|
||||
).then((result) => {
|
||||
should.exist(result);
|
||||
should(result.excludedAttrs).deepEqual(['tags']);
|
||||
should(mockPostObj.get.called).be.false();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Destroying', function () {
|
||||
it('rejects if destroying another author\'s post', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1};
|
||||
|
||||
mockPostObj.get.withArgs('author_id').returns(2);
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'destroy',
|
||||
context,
|
||||
{},
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if destroying a published post', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1};
|
||||
|
||||
mockPostObj.get.withArgs('author_id').returns(1);
|
||||
mockPostObj.get.withArgs('status').returns('published');
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'destroy',
|
||||
context,
|
||||
{},
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.calledTwice).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves if none of the above cases are true', function () {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1};
|
||||
|
||||
mockPostObj.get.withArgs('status').returns('draft');
|
||||
mockPostObj.get.withArgs('author_id').returns(1);
|
||||
|
||||
return models.Post.permissible(
|
||||
mockPostObj,
|
||||
'destroy',
|
||||
context,
|
||||
{},
|
||||
utils.permissions.contributor,
|
||||
false,
|
||||
true
|
||||
).then((result) => {
|
||||
should.exist(result);
|
||||
should(result.excludedAttrs).deepEqual(['tags']);
|
||||
should(mockPostObj.get.calledTwice).be.true();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('As Author', function () {
|
||||
describe('Editing', function () {
|
||||
it('rejects if editing another\'s post', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {author_id: 2};
|
||||
|
||||
mockPostObj.get.withArgs('author_id').returns(2);
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.author,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if changing author', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {author_id: 2};
|
||||
|
||||
mockPostObj.get.withArgs('author_id').returns(1);
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.author,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.calledTwice).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves if none of the above cases are true', function () {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {author_id: 1};
|
||||
|
||||
mockPostObj.get.withArgs('author_id').returns(1);
|
||||
|
||||
return models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.author,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
should(mockPostObj.get.calledTwice).be.true();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Adding', function () {
|
||||
it('rejects if different author id', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {author_id: 2};
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'add',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.author,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.called).be.false();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves if none of the above cases are true', function () {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {author_id: 1};
|
||||
|
||||
return models.Post.permissible(
|
||||
mockPostObj,
|
||||
'add',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.author,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
should(mockPostObj.get.called).be.false();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Everyone Else', function () {
|
||||
it('rejects if hasUserPermissions is false and not current owner', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {author_id: 2};
|
||||
|
||||
mockPostObj.get.withArgs('author_id').returns(2);
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.editor,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves if hasUserPermission is true', function () {
|
||||
var mockPostObj = {
|
||||
get: sandbox.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {author_id: 2};
|
||||
|
||||
mockPostObj.get.withArgs('author_id').returns(2);
|
||||
|
||||
return models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
utils.permissions.editor,
|
||||
true,
|
||||
true
|
||||
).then(() => {
|
||||
should(mockPostObj.get.called).be.false();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
157
core/test/unit/models/user_spec.js
Normal file
157
core/test/unit/models/user_spec.js
Normal file
|
@ -0,0 +1,157 @@
|
|||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
models = require('../../../server/models'),
|
||||
common = require('../../../server/lib/common'),
|
||||
utils = require('../../utils'),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Models: User', function () {
|
||||
before(function () {
|
||||
models.init();
|
||||
});
|
||||
|
||||
describe('Permissible', function () {
|
||||
function getUserModel(id, role) {
|
||||
var hasRole = sandbox.stub();
|
||||
|
||||
hasRole.withArgs(role).returns(true);
|
||||
|
||||
return {
|
||||
hasRole: hasRole,
|
||||
related: sandbox.stub().returns([{name: role}]),
|
||||
get: sandbox.stub().returns(id)
|
||||
};
|
||||
}
|
||||
|
||||
it('cannot delete owner', function (done) {
|
||||
var mockUser = getUserModel(1, 'Owner'),
|
||||
context = {user: 1};
|
||||
|
||||
models.User.permissible(mockUser, 'destroy', context, {}, utils.permissions.owner, true, true).then(() => {
|
||||
done(new Error('Permissible function should have errored'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockUser.hasRole.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can always edit self', function () {
|
||||
var mockUser = getUserModel(3, 'Contributor'),
|
||||
context = {user: 3};
|
||||
|
||||
return models.User.permissible(mockUser, 'edit', context, {}, utils.permissions.contributor, false, true).then(() => {
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('as editor', function () {
|
||||
it('can\'t edit another editor', function (done) {
|
||||
var mockUser = getUserModel(3, 'Editor'),
|
||||
context = {user: 2};
|
||||
|
||||
models.User.permissible(mockUser, 'edit', context, {}, utils.permissions.editor, true, true).then(() => {
|
||||
done(new Error('Permissible function should have errored'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockUser.hasRole.called).be.true();
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t edit an admin', function (done) {
|
||||
var mockUser = getUserModel(3, 'Administrator'),
|
||||
context = {user: 2};
|
||||
|
||||
models.User.permissible(mockUser, 'edit', context, {}, utils.permissions.editor, true, true).then(() => {
|
||||
done(new Error('Permissible function should have errored'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockUser.hasRole.called).be.true();
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can edit author', function () {
|
||||
var mockUser = getUserModel(3, 'Author'),
|
||||
context = {user: 2};
|
||||
|
||||
return models.User.permissible(mockUser, 'edit', context, {}, utils.permissions.editor, true, true).then(() => {
|
||||
should(mockUser.hasRole.called).be.true();
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('can edit contributor', function () {
|
||||
var mockUser = getUserModel(3, 'Contributor'),
|
||||
context = {user: 2};
|
||||
|
||||
return models.User.permissible(mockUser, 'edit', context, {}, utils.permissions.editor, true, true).then(() => {
|
||||
should(mockUser.hasRole.called).be.true();
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('can destroy self', function () {
|
||||
var mockUser = getUserModel(3, 'Editor'),
|
||||
context = {user: 3};
|
||||
|
||||
return models.User.permissible(mockUser, 'destroy', context, {}, utils.permissions.editor, true, true).then(() => {
|
||||
should(mockUser.hasRole.called).be.true();
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t destroy another editor', function (done) {
|
||||
var mockUser = getUserModel(3, 'Editor'),
|
||||
context = {user: 2};
|
||||
|
||||
models.User.permissible(mockUser, 'destroy', context, {}, utils.permissions.editor, true, true).then(() => {
|
||||
done(new Error('Permissible function should have errored'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockUser.hasRole.called).be.true();
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t destroy an admin', function (done) {
|
||||
var mockUser = getUserModel(3, 'Administrator'),
|
||||
context = {user: 2};
|
||||
|
||||
models.User.permissible(mockUser, 'destroy', context, {}, utils.permissions.editor, true, true).then(() => {
|
||||
done(new Error('Permissible function should have errored'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockUser.hasRole.called).be.true();
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can destroy an author', function () {
|
||||
var mockUser = getUserModel(3, 'Author'),
|
||||
context = {user: 2};
|
||||
|
||||
return models.User.permissible(mockUser, 'destroy', context, {}, utils.permissions.editor, true, true).then(() => {
|
||||
should(mockUser.hasRole.called).be.true();
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('can destroy a contributor', function () {
|
||||
var mockUser = getUserModel(3, 'Contributor'),
|
||||
context = {user: 2};
|
||||
|
||||
return models.User.permissible(mockUser, 'destroy', context, {}, utils.permissions.editor, true, true).then(() => {
|
||||
should(mockUser.hasRole.called).be.true();
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -177,6 +177,22 @@ DataGenerator.Content = {
|
|||
slug: 'ad-2',
|
||||
email: 'info3@ghost.org',
|
||||
password: '$2b$10$ujPIlqjTsYwfc2/zrqZXZ.yd7cQQm2iOkAFenTAJfveKkc23nwdeS'
|
||||
},
|
||||
{
|
||||
// contributor
|
||||
id: ObjectId.generate(),
|
||||
name: 'Contributor',
|
||||
slug: 'contributor',
|
||||
email: 'contributor@ghost.org',
|
||||
password: '$2b$10$ujPIlqjTsYwfc2/zrqZXZ.yd7cQQm2iOkAFenTAJfveKkc23nwdeS'
|
||||
},
|
||||
{
|
||||
// contributor
|
||||
id: ObjectId.generate(),
|
||||
name: 'contributor2',
|
||||
slug: 'contrib-2',
|
||||
email: 'contributor2@ghost.org',
|
||||
password: '$2b$10$ujPIlqjTsYwfc2/zrqZXZ.yd7cQQm2iOkAFenTAJfveKkc23nwdeS'
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -263,6 +279,11 @@ DataGenerator.Content = {
|
|||
id: ObjectId.generate(),
|
||||
name: 'Owner',
|
||||
description: 'Blog Owner'
|
||||
},
|
||||
{
|
||||
id: ObjectId.generate(),
|
||||
name: 'Contributor',
|
||||
description: 'Contributors'
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -573,14 +594,16 @@ DataGenerator.forKnex = (function () {
|
|||
createBasic(DataGenerator.Content.roles[0]),
|
||||
createBasic(DataGenerator.Content.roles[1]),
|
||||
createBasic(DataGenerator.Content.roles[2]),
|
||||
createBasic(DataGenerator.Content.roles[3])
|
||||
createBasic(DataGenerator.Content.roles[3]),
|
||||
createBasic(DataGenerator.Content.roles[4])
|
||||
];
|
||||
|
||||
users = [
|
||||
createUser(DataGenerator.Content.users[0]),
|
||||
createUser(DataGenerator.Content.users[1]),
|
||||
createUser(DataGenerator.Content.users[2]),
|
||||
createUser(DataGenerator.Content.users[3])
|
||||
createUser(DataGenerator.Content.users[3]),
|
||||
createUser(DataGenerator.Content.users[7])
|
||||
];
|
||||
|
||||
clients = [
|
||||
|
@ -610,6 +633,11 @@ DataGenerator.forKnex = (function () {
|
|||
id: ObjectId.generate(),
|
||||
user_id: DataGenerator.Content.users[3].id,
|
||||
role_id: DataGenerator.Content.roles[2].id
|
||||
},
|
||||
{
|
||||
id: ObjectId.generate(),
|
||||
user_id: DataGenerator.Content.users[7].id,
|
||||
role_id: DataGenerator.Content.roles[4].id
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -1197,6 +1197,15 @@
|
|||
"created_by": "1",
|
||||
"updated_at": "2017-09-01T12:29:50.000Z",
|
||||
"updated_by": "1"
|
||||
},
|
||||
{
|
||||
"id": "59a952be7d79ed06b0d21137",
|
||||
"name": "Contributor",
|
||||
"description": "Contributors",
|
||||
"created_at": "2017-09-01T12:29:50.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2017-09-01T12:29:50.000Z",
|
||||
"updated_by": "1"
|
||||
}
|
||||
],
|
||||
"roles_users": [
|
||||
|
|
|
@ -275,7 +275,7 @@ fixtures = {
|
|||
|
||||
createExtraUsers: function createExtraUsers() {
|
||||
// grab 3 more users
|
||||
var extraUsers = DataGenerator.Content.users.slice(2, 5);
|
||||
var extraUsers = DataGenerator.Content.users.slice(2, 6);
|
||||
|
||||
extraUsers = _.map(extraUsers, function (user) {
|
||||
return DataGenerator.forKnex.createUser(_.extend({}, user, {
|
||||
|
@ -294,7 +294,8 @@ fixtures = {
|
|||
return db.knex('roles_users').insert([
|
||||
{id: ObjectId.generate(), user_id: extraUsers[0].id, role_id: DataGenerator.Content.roles[0].id},
|
||||
{id: ObjectId.generate(), user_id: extraUsers[1].id, role_id: DataGenerator.Content.roles[1].id},
|
||||
{id: ObjectId.generate(), user_id: extraUsers[2].id, role_id: DataGenerator.Content.roles[2].id}
|
||||
{id: ObjectId.generate(), user_id: extraUsers[2].id, role_id: DataGenerator.Content.roles[2].id},
|
||||
{id: ObjectId.generate(), user_id: extraUsers[3].id, role_id: DataGenerator.Content.roles[4].id}
|
||||
]);
|
||||
});
|
||||
},
|
||||
|
@ -370,7 +371,8 @@ fixtures = {
|
|||
Administrator: DataGenerator.Content.roles[0].id,
|
||||
Editor: DataGenerator.Content.roles[1].id,
|
||||
Author: DataGenerator.Content.roles[2].id,
|
||||
Owner: DataGenerator.Content.roles[3].id
|
||||
Owner: DataGenerator.Content.roles[3].id,
|
||||
Contributor: DataGenerator.Content.roles[4].id
|
||||
};
|
||||
|
||||
// CASE: if empty db will throw SQLITE_MISUSE, hard to debug
|
||||
|
@ -976,7 +978,15 @@ module.exports = {
|
|||
owner: {context: {user: DataGenerator.Content.users[0].id}},
|
||||
admin: {context: {user: DataGenerator.Content.users[1].id}},
|
||||
editor: {context: {user: DataGenerator.Content.users[2].id}},
|
||||
author: {context: {user: DataGenerator.Content.users[3].id}}
|
||||
author: {context: {user: DataGenerator.Content.users[3].id}},
|
||||
contributor: {context: {user: DataGenerator.Content.users[7].id}}
|
||||
},
|
||||
permissions: {
|
||||
owner: {user: {roles: [DataGenerator.Content.roles[3]]}},
|
||||
admin: {user: {roles: [DataGenerator.Content.roles[0]]}},
|
||||
editor: {user: {roles: [DataGenerator.Content.roles[1]]}},
|
||||
author: {user: {roles: [DataGenerator.Content.roles[2]]}},
|
||||
contributor: {user: {roles: [DataGenerator.Content.roles[4]]}},
|
||||
},
|
||||
users: {
|
||||
ids: {
|
||||
|
@ -986,7 +996,9 @@ module.exports = {
|
|||
author: DataGenerator.Content.users[3].id,
|
||||
admin2: DataGenerator.Content.users[6].id,
|
||||
editor2: DataGenerator.Content.users[4].id,
|
||||
author2: DataGenerator.Content.users[5].id
|
||||
author2: DataGenerator.Content.users[5].id,
|
||||
contributor: DataGenerator.Content.users[7].id,
|
||||
contributor2: DataGenerator.Content.users[8].id
|
||||
}
|
||||
},
|
||||
roles: {
|
||||
|
@ -994,10 +1006,10 @@ module.exports = {
|
|||
owner: DataGenerator.Content.roles[3].id,
|
||||
admin: DataGenerator.Content.roles[0].id,
|
||||
editor: DataGenerator.Content.roles[1].id,
|
||||
author: DataGenerator.Content.roles[2].id
|
||||
author: DataGenerator.Content.roles[2].id,
|
||||
contributor: DataGenerator.Content.roles[4].id
|
||||
}
|
||||
},
|
||||
|
||||
cacheRules: {
|
||||
public: 'public, max-age=0',
|
||||
hour: 'public, max-age=' + 3600,
|
||||
|
|
Loading…
Add table
Reference in a new issue