mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Added the Admin API and tests for Personal Tokens.
no issue
This commit is contained in:
parent
be4146e324
commit
4edccfd2f7
5 changed files with 134 additions and 1 deletions
|
@ -8,6 +8,30 @@ const permissionsService = require('../../services/permissions');
|
|||
const ALLOWED_INCLUDES = ['count.posts', 'permissions', 'roles', 'roles.permissions'];
|
||||
const UNSAFE_ATTRS = ['status', 'roles'];
|
||||
|
||||
function permissionOnlySelf(frame) {
|
||||
const targetId = getTargetId(frame);
|
||||
const userId = frame.user.id;
|
||||
if (targetId !== userId) {
|
||||
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.permissions.noPermissionToAction')}));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function getTargetId(frame) {
|
||||
return frame.options.id === 'me' ? frame.user.id : frame.options.id;
|
||||
}
|
||||
|
||||
async function fetchOrCreatePersonalToken(userId) {
|
||||
const token = await models.ApiKey.findOne({user_id: userId}, {});
|
||||
|
||||
if (!token) {
|
||||
const newToken = await models.ApiKey.add({user_id: userId, type: 'admin'});
|
||||
return newToken;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
docName: 'users',
|
||||
|
||||
|
@ -176,5 +200,46 @@ module.exports = {
|
|||
query(frame) {
|
||||
return models.User.transferOwnership(frame.data.owner[0], frame.options);
|
||||
}
|
||||
},
|
||||
|
||||
readToken: {
|
||||
options: [
|
||||
'id'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
id: {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
},
|
||||
permissions: permissionOnlySelf,
|
||||
query(frame) {
|
||||
const targetId = getTargetId(frame);
|
||||
return fetchOrCreatePersonalToken(targetId);
|
||||
}
|
||||
},
|
||||
|
||||
regenerateToken: {
|
||||
headers: {
|
||||
cacheInvalidate: true
|
||||
},
|
||||
options: [
|
||||
'id'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
id: {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
},
|
||||
permissions: permissionOnlySelf,
|
||||
query(frame) {
|
||||
const targetId = getTargetId(frame);
|
||||
return fetchOrCreatePersonalToken(targetId).then((model) => {
|
||||
return models.ApiKey.refreshSecret(model.toJSON(), Object.assign({}, {id: model.id}));
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -49,5 +49,22 @@ module.exports = {
|
|||
frame.response = {
|
||||
users: models.map(model => model.toJSON(frame.options))
|
||||
};
|
||||
},
|
||||
|
||||
readToken(model, apiConfig, frame) {
|
||||
debug('readToken');
|
||||
|
||||
frame.response = {
|
||||
apiKey: model.toJSON(frame.options)
|
||||
};
|
||||
},
|
||||
|
||||
regenerateToken(model, apiConfig, frame) {
|
||||
debug('regenerateToken');
|
||||
|
||||
frame.response = {
|
||||
apiKey: model.toJSON(frame.options)
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -71,10 +71,12 @@ module.exports = function apiRoutes() {
|
|||
router.get('/users/slug/:slug', mw.authAdminApi, http(apiCanary.users.read));
|
||||
// NOTE: We don't expose any email addresses via the public api.
|
||||
router.get('/users/email/:email', mw.authAdminApi, http(apiCanary.users.read));
|
||||
router.get('/users/:id/token', mw.authAdminApi, http(apiCanary.users.readToken));
|
||||
|
||||
router.put('/users/password', mw.authAdminApi, http(apiCanary.users.changePassword));
|
||||
router.put('/users/owner', mw.authAdminApi, http(apiCanary.users.transferOwnership));
|
||||
router.put('/users/:id', mw.authAdminApi, http(apiCanary.users.edit));
|
||||
router.put('/users/:id/token', mw.authAdminApi, http(apiCanary.users.regenerateToken));
|
||||
router.del('/users/:id', mw.authAdminApi, http(apiCanary.users.destroy));
|
||||
|
||||
// ## Tags
|
||||
|
|
|
@ -334,4 +334,53 @@ describe('User API', function () {
|
|||
.set('Origin', config.get('url'))
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,7 +32,7 @@ const defaultSettings = require('../../../../core/server/data/schema/default-set
|
|||
*/
|
||||
describe('DB version integrity', function () {
|
||||
// Only these variables should need updating
|
||||
const currentSchemaHash = 'cd8820283c865acf610f97252addfa99';
|
||||
const currentSchemaHash = '6a5b00d987f045f364c6581082cd0b03';
|
||||
const currentFixturesHash = 'd46d696c94d03e41a5903500547fea77';
|
||||
const currentSettingsHash = 'b7c0c2c6a4c61561dfefe642470d30f8';
|
||||
const currentRoutesHash = '3d180d52c663d173a6be791ef411ed01';
|
||||
|
|
Loading…
Add table
Reference in a new issue