2018-02-15 22:15:43 +03:00
|
|
|
const should = require('should'),
|
2018-08-31 13:02:39 +03:00
|
|
|
url = require('url'),
|
2018-02-07 12:46:22 +03:00
|
|
|
sinon = require('sinon'),
|
2018-10-06 23:13:52 +03:00
|
|
|
Promise = require('bluebird'),
|
2018-06-26 17:00:54 +03:00
|
|
|
_ = require('lodash'),
|
|
|
|
schema = require('../../../server/data/schema'),
|
2018-02-07 12:46:22 +03:00
|
|
|
models = require('../../../server/models'),
|
2018-10-06 23:13:52 +03:00
|
|
|
permissions = require('../../../server/services/permissions'),
|
2018-02-16 02:49:15 +03:00
|
|
|
validation = require('../../../server/data/validation'),
|
2018-02-07 12:46:22 +03:00
|
|
|
common = require('../../../server/lib/common'),
|
2018-02-15 22:15:43 +03:00
|
|
|
security = require('../../../server/lib/security'),
|
2019-01-21 19:53:44 +03:00
|
|
|
testUtils = require('../../utils');
|
2018-02-07 12:46:22 +03:00
|
|
|
|
2018-02-16 02:49:15 +03:00
|
|
|
describe('Unit: models/user', function () {
|
2018-02-07 12:46:22 +03:00
|
|
|
before(function () {
|
|
|
|
models.init();
|
|
|
|
});
|
|
|
|
|
2018-06-12 21:26:16 +03:00
|
|
|
before(testUtils.teardown);
|
|
|
|
before(testUtils.setup('users:roles'));
|
|
|
|
|
2018-02-15 22:15:43 +03:00
|
|
|
afterEach(function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.restore();
|
2018-02-15 22:15:43 +03:00
|
|
|
});
|
|
|
|
|
2018-11-13 14:27:10 +03:00
|
|
|
describe('updateLastSeen method', function () {
|
|
|
|
it('exists', function () {
|
|
|
|
should.equal(typeof models.User.prototype.updateLastSeen, 'function');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('sets the last_seen property to new Date and returns a call to save', function () {
|
|
|
|
const instance = {
|
2019-01-21 19:53:44 +03:00
|
|
|
set: sinon.spy(),
|
|
|
|
save: sinon.stub().resolves()
|
2018-11-13 14:27:10 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
const now = new Date();
|
|
|
|
const clock = sinon.useFakeTimers(now.getTime());
|
|
|
|
|
|
|
|
const returnVal = models.User.prototype.updateLastSeen.call(instance);
|
|
|
|
|
|
|
|
should.deepEqual(instance.set.args[0][0], {
|
|
|
|
last_seen: now
|
|
|
|
});
|
|
|
|
|
|
|
|
should.equal(returnVal, instance.save.returnValues[0]);
|
|
|
|
|
|
|
|
clock.restore();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-02-15 22:15:43 +03:00
|
|
|
describe('validation', function () {
|
|
|
|
beforeEach(function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(security.password, 'hash').resolves('$2a$10$we16f8rpbrFZ34xWj0/ZC.LTPUux8ler7bcdTs5qIleN6srRHhilG');
|
2018-02-15 22:15:43 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('password', function () {
|
|
|
|
it('no password', function () {
|
|
|
|
return models.User.add({email: 'test1@ghost.org', name: 'Ghosty'})
|
|
|
|
.then(function (user) {
|
|
|
|
user.get('name').should.eql('Ghosty');
|
|
|
|
should.exist(user.get('password'));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('only numbers', function () {
|
|
|
|
return models.User.add({email: 'test2@ghost.org', name: 'Wursti', password: 109674836589})
|
|
|
|
.then(function (user) {
|
|
|
|
user.get('name').should.eql('Wursti');
|
|
|
|
should.exist(user.get('password'));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('can change password', function () {
|
|
|
|
let oldPassword;
|
|
|
|
|
|
|
|
return models.User.findOne({slug: 'joe-bloggs'})
|
|
|
|
.then(function (user) {
|
|
|
|
user.get('slug').should.eql('joe-bloggs');
|
|
|
|
oldPassword = user.get('password');
|
|
|
|
user.set('password', '12734!!332');
|
|
|
|
return user.save();
|
|
|
|
})
|
|
|
|
.then(function (user) {
|
|
|
|
user.get('slug').should.eql('joe-bloggs');
|
|
|
|
user.get('password').should.not.eql(oldPassword);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2018-02-16 02:49:15 +03:00
|
|
|
|
|
|
|
describe('blank', function () {
|
|
|
|
it('name cannot be blank', function () {
|
|
|
|
return models.User.add({email: 'test@ghost.org'})
|
|
|
|
.then(function () {
|
|
|
|
throw new Error('expected ValidationError');
|
|
|
|
})
|
|
|
|
.catch(function (err) {
|
|
|
|
(err instanceof common.errors.ValidationError).should.be.true;
|
|
|
|
err.message.should.match(/users\.name/);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('email cannot be blank', function () {
|
|
|
|
let data = {name: 'name'};
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(models.User, 'findOne').resolves(null);
|
2018-02-16 02:49:15 +03:00
|
|
|
|
|
|
|
return models.User.add(data)
|
|
|
|
.then(function () {
|
|
|
|
throw new Error('expected ValidationError');
|
|
|
|
})
|
|
|
|
.catch(function (err) {
|
|
|
|
err.should.be.an.Array();
|
|
|
|
(err[0] instanceof common.errors.ValidationError).should.eql.true;
|
|
|
|
err[0].message.should.match(/users\.email/);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('fn: check', function () {
|
|
|
|
beforeEach(function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(security.password, 'hash').resolves('$2a$10$we16f8rpbrFZ34xWj0/ZC.LTPUux8ler7bcdTs5qIleN6srRHhilG');
|
2018-02-16 02:49:15 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('user status is warn', function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(security.password, 'compare').resolves(true);
|
2018-02-16 02:49:15 +03:00
|
|
|
|
|
|
|
// NOTE: Add a user with a broken field to ensure we only validate changed fields on login
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(validation, 'validateSchema').resolves();
|
2018-02-16 02:49:15 +03:00
|
|
|
|
|
|
|
const user = testUtils.DataGenerator.forKnex.createUser({
|
|
|
|
status: 'warn-1',
|
|
|
|
email: 'test-9@example.de',
|
|
|
|
website: '!!!!!this-is-not-a-website!!!!'
|
|
|
|
});
|
|
|
|
|
|
|
|
return models.User.add(user)
|
|
|
|
.then(function (model) {
|
|
|
|
validation.validateSchema.restore();
|
|
|
|
|
|
|
|
return models.User.check({email: model.get('email'), password: 'test'});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('user status is active', function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(security.password, 'compare').resolves(true);
|
2018-02-16 02:49:15 +03:00
|
|
|
|
|
|
|
return models.User.check({email: testUtils.DataGenerator.Content.users[1].email, password: 'test'});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('password is incorrect', function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(security.password, 'compare').resolves(false);
|
2018-02-16 02:49:15 +03:00
|
|
|
|
|
|
|
return models.User.check({email: testUtils.DataGenerator.Content.users[1].email, password: 'test'})
|
|
|
|
.catch(function (err) {
|
|
|
|
(err instanceof common.errors.ValidationError).should.be.true;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('user not found', function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(security.password, 'compare').resolves(true);
|
2018-02-16 02:49:15 +03:00
|
|
|
|
|
|
|
return models.User.check({email: 'notfound@example.to', password: 'test'})
|
|
|
|
.catch(function (err) {
|
|
|
|
(err instanceof common.errors.NotFoundError).should.be.true;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('user not found', function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(security.password, 'compare').resolves(true);
|
2018-02-16 02:49:15 +03:00
|
|
|
|
|
|
|
return models.User.check({email: null, password: 'test'})
|
|
|
|
.catch(function (err) {
|
|
|
|
(err instanceof common.errors.NotFoundError).should.be.true;
|
|
|
|
});
|
|
|
|
});
|
2018-02-15 22:15:43 +03:00
|
|
|
});
|
|
|
|
|
2018-10-06 23:13:52 +03:00
|
|
|
describe('permissible', function () {
|
|
|
|
function getUserModel(id, role, roleId) {
|
2019-01-21 19:53:44 +03:00
|
|
|
var hasRole = sinon.stub();
|
2018-02-07 12:46:22 +03:00
|
|
|
|
|
|
|
hasRole.withArgs(role).returns(true);
|
|
|
|
|
|
|
|
return {
|
2018-10-06 23:13:52 +03:00
|
|
|
id: id,
|
2018-02-07 12:46:22 +03:00
|
|
|
hasRole: hasRole,
|
2019-01-21 19:53:44 +03:00
|
|
|
related: sinon.stub().returns([{name: role, id: roleId}]),
|
|
|
|
get: sinon.stub().returns(id)
|
2018-02-07 12:46:22 +03:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
it('cannot delete owner', function (done) {
|
|
|
|
var mockUser = getUserModel(1, 'Owner'),
|
|
|
|
context = {user: 1};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
models.User.permissible(mockUser, 'destroy', context, {}, testUtils.permissions.owner, true, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
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};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'edit', context, {}, testUtils.permissions.contributor, false, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
should(mockUser.get.calledOnce).be.true();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-10-06 23:13:52 +03:00
|
|
|
it('cannot edit my status to inactive', function () {
|
|
|
|
var mockUser = getUserModel(3, 'Editor'),
|
|
|
|
context = {user: 3};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'edit', context, {status: 'inactive'}, testUtils.permissions.editor, false, true, true)
|
2018-10-06 23:13:52 +03:00
|
|
|
.then(Promise.reject)
|
|
|
|
.catch((err) => {
|
|
|
|
err.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('without related roles', function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(models.User, 'findOne').withArgs({
|
2018-10-06 23:13:52 +03:00
|
|
|
id: 3,
|
|
|
|
status: 'all'
|
|
|
|
}, {withRelated: ['roles']}).resolves(getUserModel(3, 'Contributor'));
|
|
|
|
|
2019-01-21 19:53:44 +03:00
|
|
|
const mockUser = {id: 3, related: sinon.stub().returns()};
|
2018-10-06 23:13:52 +03:00
|
|
|
const context = {user: 3};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'edit', context, {}, testUtils.permissions.contributor, false, true, true)
|
2018-10-06 23:13:52 +03:00
|
|
|
.then(() => {
|
|
|
|
models.User.findOne.calledOnce.should.be.true();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('change role', function () {
|
|
|
|
function getUserToEdit(id, role) {
|
2019-01-21 19:53:44 +03:00
|
|
|
var hasRole = sinon.stub();
|
2018-10-06 23:13:52 +03:00
|
|
|
|
|
|
|
hasRole.withArgs(role).returns(true);
|
|
|
|
|
|
|
|
return {
|
|
|
|
id: id,
|
|
|
|
hasRole: hasRole,
|
2019-01-21 19:53:44 +03:00
|
|
|
related: sinon.stub().returns([role]),
|
|
|
|
get: sinon.stub().returns(id)
|
2018-10-06 23:13:52 +03:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
beforeEach(function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(models.User, 'getOwnerUser');
|
|
|
|
sinon.stub(permissions, 'canThis');
|
2018-10-06 23:13:52 +03:00
|
|
|
|
|
|
|
models.User.getOwnerUser.resolves({
|
|
|
|
id: testUtils.context.owner.context.user,
|
|
|
|
related: () => {
|
|
|
|
return {
|
|
|
|
at: () => {
|
|
|
|
return testUtils.permissions.owner.user.roles[0].id;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('cannot change own role', function () {
|
|
|
|
const mockUser = getUserToEdit(testUtils.context.admin.context.user, testUtils.permissions.editor.user.roles[0]);
|
|
|
|
const context = testUtils.context.admin.context;
|
|
|
|
const unsafeAttrs = testUtils.permissions.editor.user;
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'edit', context, unsafeAttrs, testUtils.permissions.admin, false, true, true)
|
2018-10-06 23:13:52 +03:00
|
|
|
.then(Promise.reject)
|
|
|
|
.catch((err) => {
|
|
|
|
err.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('is owner and does not change the role', function () {
|
|
|
|
const mockUser = getUserToEdit(testUtils.context.owner.context.user, testUtils.permissions.owner.user.roles[0]);
|
|
|
|
const context = testUtils.context.owner.context;
|
|
|
|
const unsafeAttrs = testUtils.permissions.owner.user;
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'edit', context, unsafeAttrs, testUtils.permissions.owner, false, true, true)
|
2018-10-06 23:13:52 +03:00
|
|
|
.then(() => {
|
|
|
|
models.User.getOwnerUser.calledOnce.should.be.true();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('cannot change owner\'s role', function () {
|
|
|
|
const mockUser = getUserToEdit(testUtils.context.owner.context.user, testUtils.permissions.owner.user.roles[0]);
|
|
|
|
const context = testUtils.context.admin.context;
|
|
|
|
const unsafeAttrs = testUtils.permissions.editor.user;
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'edit', context, unsafeAttrs, testUtils.permissions.admin, false, true, true)
|
2018-10-06 23:13:52 +03:00
|
|
|
.then(Promise.reject)
|
|
|
|
.catch((err) => {
|
|
|
|
err.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('admin can change author role', function () {
|
|
|
|
const mockUser = getUserToEdit(testUtils.context.author.context.user, testUtils.permissions.author.user.roles[0]);
|
|
|
|
const context = testUtils.context.admin.context;
|
|
|
|
const unsafeAttrs = testUtils.permissions.editor.user;
|
|
|
|
|
|
|
|
permissions.canThis.returns({
|
|
|
|
assign: {
|
2019-01-21 19:53:44 +03:00
|
|
|
role: sinon.stub().resolves()
|
2018-10-06 23:13:52 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'edit', context, unsafeAttrs, testUtils.permissions.admin, true, true, true)
|
2018-10-06 23:13:52 +03:00
|
|
|
.then(() => {
|
|
|
|
models.User.getOwnerUser.calledOnce.should.be.true();
|
|
|
|
permissions.canThis.calledOnce.should.be.true();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('author can\'t change admin role', function () {
|
|
|
|
const mockUser = getUserToEdit(testUtils.context.admin.context.user, testUtils.permissions.admin.user.roles[0]);
|
|
|
|
const context = testUtils.context.author.context;
|
|
|
|
const unsafeAttrs = testUtils.permissions.editor.user;
|
|
|
|
|
|
|
|
permissions.canThis.returns({
|
|
|
|
assign: {
|
2019-01-21 19:53:44 +03:00
|
|
|
role: sinon.stub().resolves()
|
2018-10-06 23:13:52 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'edit', context, unsafeAttrs, testUtils.permissions.author, false, true, true)
|
2018-10-06 23:13:52 +03:00
|
|
|
.then(Promise.reject)
|
|
|
|
.catch((err) => {
|
|
|
|
err.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-02-07 12:46:22 +03:00
|
|
|
describe('as editor', function () {
|
|
|
|
it('can\'t edit another editor', function (done) {
|
|
|
|
var mockUser = getUserModel(3, 'Editor'),
|
|
|
|
context = {user: 2};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
models.User.permissible(mockUser, 'edit', context, {}, testUtils.permissions.editor, true, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
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();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-10-06 23:13:52 +03:00
|
|
|
it('can\'t edit owner', function (done) {
|
|
|
|
var mockUser = getUserModel(3, 'Owner'),
|
|
|
|
context = {user: 2};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
models.User.permissible(mockUser, 'edit', context, {}, testUtils.permissions.editor, true, true, true).then(() => {
|
2018-10-06 23:13:52 +03:00
|
|
|
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();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-02-07 12:46:22 +03:00
|
|
|
it('can\'t edit an admin', function (done) {
|
|
|
|
var mockUser = getUserModel(3, 'Administrator'),
|
|
|
|
context = {user: 2};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
models.User.permissible(mockUser, 'edit', context, {}, testUtils.permissions.editor, true, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
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};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'edit', context, {}, testUtils.permissions.editor, true, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
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};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'edit', context, {}, testUtils.permissions.editor, true, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
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};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'destroy', context, {}, testUtils.permissions.editor, true, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
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};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
models.User.permissible(mockUser, 'destroy', context, {}, testUtils.permissions.editor, true, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
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};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
models.User.permissible(mockUser, 'destroy', context, {}, testUtils.permissions.editor, true, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
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};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'destroy', context, {}, testUtils.permissions.editor, true, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
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};
|
|
|
|
|
2019-01-18 15:39:53 +03:00
|
|
|
return models.User.permissible(mockUser, 'destroy', context, {}, testUtils.permissions.editor, true, true, true).then(() => {
|
2018-02-07 12:46:22 +03:00
|
|
|
should(mockUser.hasRole.called).be.true();
|
|
|
|
should(mockUser.get.calledOnce).be.true();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2018-03-05 11:10:27 +03:00
|
|
|
|
2018-04-05 16:59:52 +03:00
|
|
|
describe('Fetch', function () {
|
|
|
|
before(function () {
|
|
|
|
models.init();
|
|
|
|
});
|
|
|
|
|
|
|
|
after(function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.restore();
|
2018-04-05 16:59:52 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('ensure data type', function () {
|
|
|
|
return models.User.findOne({slug: 'joe-bloggs'}, testUtils.context.internal)
|
|
|
|
.then((user) => {
|
|
|
|
user.get('updated_by').should.be.a.String();
|
|
|
|
user.get('created_by').should.be.a.String();
|
|
|
|
user.get('created_at').should.be.a.Date();
|
|
|
|
user.get('updated_at').should.be.a.Date();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-03-05 11:10:27 +03:00
|
|
|
describe('Edit', function () {
|
|
|
|
before(function () {
|
|
|
|
models.init();
|
|
|
|
});
|
|
|
|
|
|
|
|
after(function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.restore();
|
2018-03-05 11:10:27 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('resets given empty value to null', function () {
|
|
|
|
return models.User.findOne({slug: 'joe-bloggs'})
|
|
|
|
.then(function (user) {
|
|
|
|
user.get('slug').should.eql('joe-bloggs');
|
|
|
|
user.get('profile_image').should.eql('https://example.com/super_photo.jpg');
|
|
|
|
user.set('profile_image', '');
|
|
|
|
user.set('bio', '');
|
|
|
|
return user.save();
|
|
|
|
})
|
|
|
|
.then(function (user) {
|
|
|
|
should(user.get('profile_image')).be.null();
|
|
|
|
user.get('bio').should.eql('');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2018-06-26 17:00:54 +03:00
|
|
|
|
|
|
|
describe('Add', function () {
|
|
|
|
const events = {
|
|
|
|
user: []
|
|
|
|
};
|
|
|
|
|
|
|
|
before(function () {
|
|
|
|
models.init();
|
|
|
|
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(models.User.prototype, 'emitChange').callsFake(function (event) {
|
2018-06-26 17:00:54 +03:00
|
|
|
events.user.push({event: event, data: this.toJSON()});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
after(function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.restore();
|
2018-06-26 17:00:54 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('defaults', function () {
|
|
|
|
return models.User.add({slug: 'joe', name: 'Joe', email: 'joe@test.com'})
|
|
|
|
.then(function (user) {
|
|
|
|
user.get('name').should.eql('Joe');
|
|
|
|
user.get('email').should.eql('joe@test.com');
|
|
|
|
user.get('slug').should.eql('joe');
|
|
|
|
user.get('visibility').should.eql('public');
|
|
|
|
user.get('status').should.eql('active');
|
|
|
|
|
|
|
|
_.each(_.keys(schema.tables.users), (key) => {
|
|
|
|
should.exist(events.user[0].data.hasOwnProperty(key));
|
|
|
|
|
|
|
|
if (['status', 'visibility'].indexOf(key) !== -1) {
|
|
|
|
events.user[0].data[key].should.eql(schema.tables.users[key].defaultTo);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2018-10-06 23:13:52 +03:00
|
|
|
|
|
|
|
describe('transferOwnership', function () {
|
|
|
|
let ownerRole;
|
|
|
|
|
|
|
|
beforeEach(function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
ownerRole = sinon.stub();
|
2018-10-06 23:13:52 +03:00
|
|
|
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(models.Role, 'findOne');
|
2018-10-06 23:13:52 +03:00
|
|
|
|
|
|
|
models.Role.findOne
|
|
|
|
.withArgs({name: 'Owner'})
|
|
|
|
.resolves(testUtils.permissions.owner.user.roles[0]);
|
|
|
|
|
|
|
|
models.Role.findOne
|
|
|
|
.withArgs({name: 'Administrator'})
|
|
|
|
.resolves(testUtils.permissions.admin.user.roles[0]);
|
|
|
|
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(models.User, 'findOne');
|
2018-10-06 23:13:52 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Cannot transfer ownership if not owner', function () {
|
|
|
|
const loggedInUser = testUtils.context.admin;
|
|
|
|
const userToChange = loggedInUser;
|
2019-01-21 19:53:44 +03:00
|
|
|
const contextUser = sinon.stub();
|
2018-10-06 23:13:52 +03:00
|
|
|
|
2019-01-21 19:53:44 +03:00
|
|
|
contextUser.toJSON = sinon.stub().returns(testUtils.permissions.admin.user);
|
2018-10-06 23:13:52 +03:00
|
|
|
|
|
|
|
models.User
|
|
|
|
.findOne
|
|
|
|
.withArgs({id: loggedInUser.context.user}, {withRelated: ['roles']})
|
|
|
|
.resolves(contextUser);
|
|
|
|
|
|
|
|
return models.User.transferOwnership({id: loggedInUser.context.user}, loggedInUser)
|
|
|
|
.then(Promise.reject)
|
|
|
|
.catch((err) => {
|
|
|
|
err.should.be.an.instanceof(common.errors.NoPermissionError);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Owner tries to transfer ownership to author', function () {
|
|
|
|
const loggedInUser = testUtils.context.owner;
|
|
|
|
const userToChange = testUtils.context.editor;
|
2019-01-21 19:53:44 +03:00
|
|
|
const contextUser = sinon.stub();
|
2018-10-06 23:13:52 +03:00
|
|
|
|
2019-01-21 19:53:44 +03:00
|
|
|
contextUser.toJSON = sinon.stub().returns(testUtils.permissions.owner.user);
|
2018-10-06 23:13:52 +03:00
|
|
|
|
|
|
|
models.User
|
|
|
|
.findOne
|
|
|
|
.withArgs({id: loggedInUser.context.user}, {withRelated: ['roles']})
|
|
|
|
.resolves(contextUser);
|
|
|
|
|
|
|
|
models.User
|
|
|
|
.findOne
|
|
|
|
.withArgs({id: userToChange.context.user}, {withRelated: ['roles']})
|
|
|
|
.resolves(contextUser);
|
|
|
|
|
|
|
|
return models.User.transferOwnership({id: userToChange.context.user}, loggedInUser)
|
|
|
|
.then(Promise.reject)
|
|
|
|
.catch((err) => {
|
|
|
|
err.should.be.an.instanceof(common.errors.ValidationError);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('isSetup', function () {
|
|
|
|
it('active', function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(models.User, 'getOwnerUser').resolves({get: sinon.stub().returns('active')});
|
2018-10-06 23:13:52 +03:00
|
|
|
|
|
|
|
return models.User.isSetup()
|
|
|
|
.then((result) => {
|
|
|
|
result.should.be.true();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('inactive', function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(models.User, 'getOwnerUser').resolves({get: sinon.stub().returns('inactive')});
|
2018-10-06 23:13:52 +03:00
|
|
|
|
|
|
|
return models.User.isSetup()
|
|
|
|
.then((result) => {
|
|
|
|
result.should.be.false();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2018-02-07 12:46:22 +03:00
|
|
|
});
|