2018-10-13 00:27:30 +03:00
|
|
|
const should = require('should');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const supertest = require('supertest');
|
|
|
|
const Promise = require('bluebird');
|
2019-09-20 18:02:45 +03:00
|
|
|
const testUtils = require('../../utils');
|
2020-05-27 20:47:53 +03:00
|
|
|
const config = require('../../../core/shared/config');
|
2020-03-30 18:26:47 +03:00
|
|
|
const db = require('../../../core/server/data/db');
|
|
|
|
const models = require('../../../core/server/models');
|
2018-10-13 00:27:30 +03:00
|
|
|
const localUtils = require('./utils');
|
2019-09-20 18:02:45 +03:00
|
|
|
|
2018-10-13 00:27:30 +03:00
|
|
|
const ghost = testUtils.startGhost;
|
|
|
|
let request;
|
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
describe('User API', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
let inactiveUser;
|
|
|
|
let admin;
|
2019-02-04 17:16:24 +03:00
|
|
|
|
|
|
|
before(function () {
|
|
|
|
return ghost()
|
2019-09-10 12:41:42 +03:00
|
|
|
.then(function () {
|
2019-02-04 17:16:24 +03:00
|
|
|
request = supertest.agent(config.get('url'));
|
|
|
|
})
|
|
|
|
.then(function () {
|
|
|
|
// create inactive user
|
|
|
|
return testUtils.createUser({
|
|
|
|
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+3@ghost.org', status: 'inactive'}),
|
|
|
|
role: testUtils.DataGenerator.Content.roles[2].name
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
2019-02-04 17:16:24 +03:00
|
|
|
})
|
|
|
|
.then(function (_user) {
|
|
|
|
inactiveUser = _user;
|
|
|
|
|
|
|
|
// create admin user
|
|
|
|
return testUtils.createUser({
|
|
|
|
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+admin@ghost.org', slug: 'admin'}),
|
|
|
|
role: testUtils.DataGenerator.Content.roles[0].name
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.then(function (_user) {
|
|
|
|
admin = _user;
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
// by default we login with the owner
|
|
|
|
return localUtils.doAuth(request);
|
|
|
|
});
|
|
|
|
});
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
it('Can request all users ordered by id', function (done) {
|
|
|
|
// @NOTE: ASC is default
|
|
|
|
request.get(localUtils.API.getApiQuery('users/?order=id%20DESC'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2020-04-29 18:44:27 +03:00
|
|
|
const jsonResponse = res.body;
|
2019-02-04 17:16:24 +03:00
|
|
|
should.exist(jsonResponse.users);
|
|
|
|
localUtils.API.checkResponse(jsonResponse, 'users');
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
// owner use + ghost-author user when Ghost starts
|
|
|
|
// and two extra users, see createUser in before
|
|
|
|
jsonResponse.users.should.have.length(4);
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-03-06 12:17:41 +03:00
|
|
|
localUtils.API.checkResponse(jsonResponse.users[0], 'user');
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
jsonResponse.users[0].email.should.eql(admin.email);
|
|
|
|
jsonResponse.users[0].status.should.eql(admin.status);
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
jsonResponse.users[1].email.should.eql(inactiveUser.email);
|
|
|
|
jsonResponse.users[1].status.should.eql(inactiveUser.status);
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
jsonResponse.users[2].email.should.eql('ghost-author@example.com');
|
|
|
|
jsonResponse.users[3].email.should.eql(testUtils.DataGenerator.Content.users[0].email);
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
testUtils.API.isISO8601(jsonResponse.users[3].last_seen).should.be.true();
|
|
|
|
testUtils.API.isISO8601(jsonResponse.users[3].created_at).should.be.true();
|
|
|
|
testUtils.API.isISO8601(jsonResponse.users[3].updated_at).should.be.true();
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-09-10 12:41:42 +03:00
|
|
|
// only "ghost" author has a published post
|
|
|
|
jsonResponse.users[0].url.should.eql(`${config.get('url')}/404/`);
|
2019-02-04 17:16:24 +03:00
|
|
|
jsonResponse.users[1].url.should.eql(`${config.get('url')}/404/`);
|
|
|
|
jsonResponse.users[2].url.should.eql(`${config.get('url')}/author/ghost/`);
|
2019-09-10 12:41:42 +03:00
|
|
|
jsonResponse.users[3].url.should.eql(`${config.get('url')}/404/`);
|
2018-10-18 12:05:51 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
done();
|
2018-10-18 12:05:51 +03:00
|
|
|
});
|
2019-02-04 17:16:24 +03:00
|
|
|
});
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
it('Can include user roles', function (done) {
|
|
|
|
request.get(localUtils.API.getApiQuery('users/?include=roles'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
2020-04-29 18:44:27 +03:00
|
|
|
const jsonResponse = res.body;
|
2019-02-04 17:16:24 +03:00
|
|
|
should.exist(jsonResponse.users);
|
|
|
|
localUtils.API.checkResponse(jsonResponse, 'users');
|
|
|
|
|
|
|
|
jsonResponse.users.should.have.length(4);
|
2019-03-06 12:17:41 +03:00
|
|
|
localUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
2019-02-04 17:16:24 +03:00
|
|
|
done();
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
2019-02-04 17:16:24 +03:00
|
|
|
});
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
it('Can paginate users', function (done) {
|
|
|
|
request.get(localUtils.API.getApiQuery('users/?page=2'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.equal(jsonResponse.meta.pagination.page, 2);
|
|
|
|
done();
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
2019-02-04 17:16:24 +03:00
|
|
|
});
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
it('Can retrieve a user by id', function (done) {
|
|
|
|
request.get(localUtils.API.getApiQuery('users/' + testUtils.existingData.users[0].id + '/?include=roles,roles.permissions,count.posts'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
2020-04-29 18:44:27 +03:00
|
|
|
const jsonResponse = res.body;
|
2019-02-04 17:16:24 +03:00
|
|
|
should.exist(jsonResponse.users);
|
|
|
|
should.not.exist(jsonResponse.meta);
|
|
|
|
|
|
|
|
jsonResponse.users.should.have.length(1);
|
2019-03-06 12:17:41 +03:00
|
|
|
localUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles', 'count']);
|
2019-02-04 17:16:24 +03:00
|
|
|
localUtils.API.checkResponse(jsonResponse.users[0].roles[0], 'role', ['permissions']);
|
2020-08-31 08:29:58 +03:00
|
|
|
|
|
|
|
should.exist(jsonResponse.users[0].count.posts);
|
|
|
|
jsonResponse.users[0].count.posts.should.equal(0);
|
2019-02-04 17:16:24 +03:00
|
|
|
done();
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
2019-02-04 17:16:24 +03:00
|
|
|
});
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
it('Can retrieve a user by slug', function (done) {
|
|
|
|
request.get(localUtils.API.getApiQuery('users/slug/joe-bloggs/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
2020-04-29 18:44:27 +03:00
|
|
|
const jsonResponse = res.body;
|
2019-02-04 17:16:24 +03:00
|
|
|
should.exist(jsonResponse.users);
|
|
|
|
should.not.exist(jsonResponse.meta);
|
|
|
|
|
|
|
|
jsonResponse.users.should.have.length(1);
|
2019-03-06 12:17:41 +03:00
|
|
|
localUtils.API.checkResponse(jsonResponse.users[0], 'user');
|
2019-02-04 17:16:24 +03:00
|
|
|
done();
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
2019-02-04 17:16:24 +03:00
|
|
|
});
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
it('Can retrieve a user by email', function (done) {
|
|
|
|
request.get(localUtils.API.getApiQuery('users/email/jbloggs%40example.com/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
2020-04-29 18:44:27 +03:00
|
|
|
const jsonResponse = res.body;
|
2019-02-04 17:16:24 +03:00
|
|
|
should.exist(jsonResponse.users);
|
|
|
|
should.not.exist(jsonResponse.meta);
|
|
|
|
|
|
|
|
jsonResponse.users.should.have.length(1);
|
2019-03-06 12:17:41 +03:00
|
|
|
localUtils.API.checkResponse(jsonResponse.users[0], 'user');
|
2019-02-04 17:16:24 +03:00
|
|
|
done();
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
2019-02-04 17:16:24 +03:00
|
|
|
});
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2020-10-19 07:45:26 +03:00
|
|
|
it('can edit a user', function () {
|
|
|
|
return request.put(localUtils.API.getApiQuery('users/me/'))
|
2019-02-04 17:16:24 +03:00
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.send({
|
|
|
|
users: [{
|
|
|
|
website: 'http://joe-bloggs.ghost.org',
|
|
|
|
password: 'mynewfancypasswordwhichisnotallowed'
|
|
|
|
}]
|
|
|
|
})
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
2020-10-19 07:45:26 +03:00
|
|
|
.then(function (res) {
|
2020-04-29 18:44:27 +03:00
|
|
|
const putBody = res.body;
|
2019-02-04 17:16:24 +03:00
|
|
|
res.headers['x-cache-invalidate'].should.eql('/*');
|
|
|
|
should.exist(putBody.users[0]);
|
|
|
|
putBody.users[0].website.should.eql('http://joe-bloggs.ghost.org');
|
|
|
|
putBody.users[0].email.should.eql('jbloggs@example.com');
|
2019-03-06 12:17:41 +03:00
|
|
|
localUtils.API.checkResponse(putBody.users[0], 'user');
|
2019-02-04 17:16:24 +03:00
|
|
|
|
|
|
|
should.not.exist(putBody.users[0].password);
|
|
|
|
|
2020-10-19 07:45:26 +03:00
|
|
|
return models.User.findOne({id: putBody.users[0].id})
|
2019-02-04 17:16:24 +03:00
|
|
|
.then((user) => {
|
|
|
|
return models.User.isPasswordCorrect({
|
|
|
|
plainPassword: 'mynewfancypasswordwhichisnotallowed',
|
|
|
|
hashedPassword: user.get('password')
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.then(Promise.reject)
|
|
|
|
.catch((err) => {
|
|
|
|
err.code.should.eql('PASSWORD_INCORRECT');
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
|
|
|
});
|
2019-02-04 17:16:24 +03:00
|
|
|
});
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
it('Can destroy an active user', function () {
|
|
|
|
const userId = testUtils.existingData.users[1].id;
|
2018-10-13 00:27:30 +03:00
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
return request
|
|
|
|
.get(localUtils.API.getApiQuery(`posts/?filter=author_id:${userId}`))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect(200)
|
|
|
|
.then((res) => {
|
|
|
|
res.body.posts.length.should.eql(7);
|
2019-01-21 14:36:13 +03:00
|
|
|
|
2018-10-13 00:27:30 +03:00
|
|
|
return request
|
2019-02-04 17:16:24 +03:00
|
|
|
.delete(localUtils.API.getApiQuery(`users/${userId}`))
|
2018-10-13 00:27:30 +03:00
|
|
|
.set('Origin', config.get('url'))
|
2019-12-16 10:23:46 +03:00
|
|
|
.expect(200);
|
2019-02-04 17:16:24 +03:00
|
|
|
})
|
2019-12-16 10:23:46 +03:00
|
|
|
.then((res) => {
|
|
|
|
should.exist(res.body.meta.filename);
|
|
|
|
|
2019-12-17 10:37:11 +03:00
|
|
|
return request
|
|
|
|
.get(localUtils.API.getApiQuery(`db/?filename=${res.body.meta.filename}/`))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect(200);
|
|
|
|
})
|
|
|
|
.then(() => {
|
2018-10-13 00:27:30 +03:00
|
|
|
return request
|
2019-02-04 17:16:24 +03:00
|
|
|
.get(localUtils.API.getApiQuery(`users/${userId}/`))
|
2018-10-13 00:27:30 +03:00
|
|
|
.set('Origin', config.get('url'))
|
2019-02-04 17:16:24 +03:00
|
|
|
.expect(404);
|
|
|
|
})
|
|
|
|
.then(() => {
|
2018-10-13 00:27:30 +03:00
|
|
|
return request
|
2019-02-04 17:16:24 +03:00
|
|
|
.get(localUtils.API.getApiQuery(`posts/?filter=author_id:${userId}`))
|
2018-10-13 00:27:30 +03:00
|
|
|
.set('Origin', config.get('url'))
|
2019-02-04 17:16:24 +03:00
|
|
|
.expect(200);
|
|
|
|
})
|
|
|
|
.then((res) => {
|
|
|
|
res.body.posts.length.should.eql(0);
|
|
|
|
|
|
|
|
return db.knex('roles_users')
|
|
|
|
.where({
|
|
|
|
user_id: userId
|
2018-10-13 00:27:30 +03:00
|
|
|
})
|
2019-02-04 17:16:24 +03:00
|
|
|
.select();
|
|
|
|
})
|
2020-10-19 07:45:26 +03:00
|
|
|
.then((rolesUsersModels) => {
|
|
|
|
rolesUsersModels.length.should.eql(0);
|
2019-02-04 17:16:24 +03:00
|
|
|
|
|
|
|
return db.knex('roles_users')
|
|
|
|
.select();
|
|
|
|
})
|
2020-10-19 07:45:26 +03:00
|
|
|
.then((rolesUsers) => {
|
|
|
|
rolesUsers.length.should.greaterThan(0);
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-02-04 17:16:24 +03:00
|
|
|
it('Can transfer ownership to admin user', function () {
|
|
|
|
return request
|
|
|
|
.put(localUtils.API.getApiQuery('users/owner'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.send({
|
|
|
|
owner: [{
|
|
|
|
id: admin.id
|
|
|
|
}]
|
|
|
|
})
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.then((res) => {
|
|
|
|
res.body.users[0].roles[0].name.should.equal(testUtils.DataGenerator.Content.roles[0].name);
|
|
|
|
res.body.users[1].roles[0].name.should.equal(testUtils.DataGenerator.Content.roles[3].name);
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-03-05 13:22:32 +03:00
|
|
|
it('Can change password and retain the session', async function () {
|
|
|
|
await request
|
2019-02-04 17:16:24 +03:00
|
|
|
.put(localUtils.API.getApiQuery('users/password'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.send({
|
|
|
|
password: [{
|
|
|
|
newPassword: '1234abcde!!',
|
|
|
|
ne2Password: '1234abcde!!',
|
|
|
|
oldPassword: 'Sl1m3rson99',
|
|
|
|
user_id: testUtils.existingData.users[0].id
|
|
|
|
}]
|
|
|
|
})
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.then((res) => {
|
|
|
|
should.exist(res.body.password);
|
|
|
|
should.exist(res.body.password[0].message);
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
2020-03-05 13:22:32 +03:00
|
|
|
|
|
|
|
await request
|
|
|
|
.get(localUtils.API.getApiQuery('session/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect(200);
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|
2020-10-26 19:27:12 +03:00
|
|
|
|
|
|
|
it('Can read the user\'s Personal Token', async function () {
|
|
|
|
await request
|
|
|
|
.get(localUtils.API.getApiQuery('users/me/token/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.then((res) => {
|
|
|
|
should.exist(res.body.apiKey);
|
|
|
|
should.exist(res.body.apiKey.id);
|
|
|
|
should.exist(res.body.apiKey.secret);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Can\'t read another user\'s Personal Token', async function () {
|
|
|
|
const userNotAdmin = testUtils.existingData.users.find(user => user.email === 'ghost-author@example.com');
|
|
|
|
await request
|
|
|
|
.get(localUtils.API.getApiQuery('users/' + userNotAdmin.id + '/token/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(403)
|
|
|
|
.then((res) => {
|
|
|
|
should.exist(res.body.errors);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Can re-generate the user\'s Personal Token', async function () {
|
|
|
|
const {body: {apiKey: {id, secret}}} = await request
|
|
|
|
.get(localUtils.API.getApiQuery('users/me/token/'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
await request
|
|
|
|
.put(localUtils.API.getApiQuery('users/me/token'))
|
|
|
|
.set('Origin', config.get('url'))
|
|
|
|
.expect('Content-Type', /json/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(200)
|
|
|
|
.then((res) => {
|
|
|
|
should.exist(res.body.apiKey);
|
|
|
|
should.exist(res.body.apiKey.id);
|
|
|
|
should.exist(res.body.apiKey.secret);
|
|
|
|
|
|
|
|
should(res.body.id).not.be.equal(id);
|
|
|
|
should(res.body.secret).not.be.equal(secret);
|
|
|
|
});
|
|
|
|
});
|
2018-10-13 00:27:30 +03:00
|
|
|
});
|