mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Created DB Backup integration (#10974)
* Simplified db controller permissions options The existing objects were confusing because they did the same thing as setting permissions to true, but gave the impressions that something special was happening/required. * Added DB Backup Integration Role This will allow us to assign certain api_keys this role, in order to automate db backups * Allowed admin api_keys to have configurable roles This will allow keys for the admin api to do customised things such as db export * Added ghost-backup integration to fixtures * Added migrations for DB Backup Integration and role
This commit is contained in:
parent
5f9f5ea0d5
commit
21427ad73f
8 changed files with 188 additions and 20 deletions
|
@ -46,9 +46,7 @@ module.exports = {
|
|||
value: () => (exporter.fileName())
|
||||
}
|
||||
},
|
||||
permissions: {
|
||||
method: 'exportContent'
|
||||
},
|
||||
permissions: true,
|
||||
query(frame) {
|
||||
return Promise.resolve()
|
||||
.then(() => exporter.doExport({include: frame.options.withRelated}))
|
||||
|
@ -69,9 +67,7 @@ module.exports = {
|
|||
}
|
||||
}
|
||||
},
|
||||
permissions: {
|
||||
method: 'importContent'
|
||||
},
|
||||
permissions: true,
|
||||
query(frame) {
|
||||
return importer.importFromFile(frame.file, {include: frame.options.withRelated});
|
||||
}
|
||||
|
@ -79,9 +75,7 @@ module.exports = {
|
|||
|
||||
deleteAllContent: {
|
||||
statusCode: 204,
|
||||
permissions: {
|
||||
method: 'deleteAllContent'
|
||||
},
|
||||
permissions: true,
|
||||
query() {
|
||||
/**
|
||||
* @NOTE:
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
const logging = require('../../../../lib/common/logging');
|
||||
const merge = require('lodash/merge');
|
||||
const models = require('../../../../models');
|
||||
const utils = require('../../../schema/fixtures/utils');
|
||||
|
||||
const _private = {};
|
||||
|
||||
_private.printResult = function printResult(result, message) {
|
||||
if (result.done === result.expected) {
|
||||
logging.info(message);
|
||||
} else {
|
||||
logging.warn(`(${result.done}/${result.expected}) ${message}`);
|
||||
}
|
||||
};
|
||||
|
||||
_private.addApiKeyRole = (options) => {
|
||||
const message = 'Adding "DB Backup Integration" role to roles table';
|
||||
const apiKeyRole = utils.findModelFixtureEntry('Role', {name: 'DB Backup Integration'});
|
||||
|
||||
return models.Role.findOne({name: apiKeyRole.name}, options)
|
||||
.then((role) => {
|
||||
if (!role) {
|
||||
return utils.addFixturesForModel({
|
||||
name: 'Role',
|
||||
entries: [apiKeyRole]
|
||||
}, options).then(result => _private.printResult(result, message));
|
||||
}
|
||||
|
||||
logging.warn(message);
|
||||
});
|
||||
};
|
||||
|
||||
_private.addApiKeyPermissions = (options) => {
|
||||
const message = 'Adding permissions for the "DB Backup Integration" role';
|
||||
const relations = utils.findRelationFixture('Role', 'Permission');
|
||||
|
||||
return utils.addFixturesForRelation({
|
||||
from: relations.from,
|
||||
to: relations.to,
|
||||
entries: {
|
||||
'DB Backup Integration': relations.entries['DB Backup Integration']
|
||||
}
|
||||
}, options).then(result => _private.printResult(result, message));
|
||||
};
|
||||
|
||||
_private.removeApiKeyPermissionsAndRole = (options) => {
|
||||
const message = 'Rollback: Removing "DB Backup Integration" role and permissions';
|
||||
|
||||
return models.Role.findOne({name: 'DB Backup Integration'}, options)
|
||||
.then((role) => {
|
||||
if (!role) {
|
||||
logging.warn(message);
|
||||
return;
|
||||
}
|
||||
|
||||
return role.destroy().then(() => {
|
||||
logging.info(message);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.config = {
|
||||
transaction: true
|
||||
};
|
||||
|
||||
module.exports.up = (options) => {
|
||||
const localOptions = merge({
|
||||
context: {internal: true},
|
||||
migrating: true
|
||||
}, options);
|
||||
|
||||
return _private.addApiKeyRole(localOptions)
|
||||
.then(() => _private.addApiKeyPermissions(localOptions));
|
||||
};
|
||||
|
||||
module.exports.down = (options) => {
|
||||
const localOptions = merge({
|
||||
context: {internal: true},
|
||||
migrating: true
|
||||
}, options);
|
||||
|
||||
return _private.removeApiKeyPermissionsAndRole(localOptions);
|
||||
};
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
const logging = require('../../../../lib/common/logging');
|
||||
const merge = require('lodash/merge');
|
||||
const models = require('../../../../models');
|
||||
const utils = require('../../../schema/fixtures/utils');
|
||||
|
||||
const _private = {};
|
||||
|
||||
_private.printResult = function printResult(result, message) {
|
||||
if (result.done === result.expected) {
|
||||
logging.info(message);
|
||||
} else {
|
||||
logging.warn(`(${result.done}/${result.expected}) ${message}`);
|
||||
}
|
||||
};
|
||||
|
||||
_private.addGhostBackupIntegration = (options) => {
|
||||
const message = 'Adding "Ghost Backup DB" integration';
|
||||
const fixtureIntegration = utils.findModelFixtureEntry('Integration', {slug: 'ghost-backup'});
|
||||
|
||||
return models.Integration.findOne({slug: fixtureIntegration.slug}, options)
|
||||
.then((integration) => {
|
||||
if (!integration) {
|
||||
return utils.addFixturesForModel({
|
||||
name: 'Integration',
|
||||
entries: [fixtureIntegration]
|
||||
}, options).then(result => _private.printResult(result, message));
|
||||
}
|
||||
|
||||
logging.warn(message);
|
||||
});
|
||||
};
|
||||
|
||||
_private.removeGhostBackupIntegration = (options) => {
|
||||
const message = 'Rollback: Removing "Ghost Backup DB" integration';
|
||||
|
||||
return models.Integration.findOne({slug: 'ghost-backup'}, options)
|
||||
.then((integration) => {
|
||||
if (!integration) {
|
||||
logging.warn(message);
|
||||
return;
|
||||
}
|
||||
|
||||
return integration.destroy().then(() => {
|
||||
logging.info(message);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.config = {
|
||||
transaction: true
|
||||
};
|
||||
|
||||
module.exports.up = (options) => {
|
||||
const localOptions = merge({
|
||||
context: {internal: true},
|
||||
migrating: true
|
||||
}, options);
|
||||
|
||||
return _private.addGhostBackupIntegration(localOptions);
|
||||
};
|
||||
|
||||
module.exports.down = (options) => {
|
||||
const localOptions = merge({
|
||||
context: {internal: true},
|
||||
migrating: true
|
||||
}, options);
|
||||
|
||||
return _private.removeGhostBackupIntegration(localOptions);
|
||||
};
|
||||
|
|
@ -66,6 +66,10 @@
|
|||
{
|
||||
"name": "Admin Integration",
|
||||
"description": "External Apps"
|
||||
},
|
||||
{
|
||||
"name": "DB Backup Integration",
|
||||
"description": "Internal DB Backup Client"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -568,6 +572,13 @@
|
|||
"description": "Built-in Zapier integration",
|
||||
"type": "builtin",
|
||||
"api_keys": [{"type": "admin"}]
|
||||
},
|
||||
{
|
||||
"slug": "ghost-backup",
|
||||
"name": "Ghost Backup",
|
||||
"description": "Internal DB Backup integration",
|
||||
"type": "internal",
|
||||
"api_keys": [{"type": "admin", "role": "DB Backup Integration"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -605,6 +616,9 @@
|
|||
"action": "all",
|
||||
"member": "all"
|
||||
},
|
||||
"DB Backup Integration": {
|
||||
"db": "all"
|
||||
},
|
||||
"Admin Integration": {
|
||||
"mail": "all",
|
||||
"notification": "all",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
const omit = require('lodash/omit');
|
||||
const crypto = require('crypto');
|
||||
const ghostBookshelf = require('./base');
|
||||
const {Role} = require('./role');
|
||||
|
@ -44,6 +45,10 @@ const ApiKey = ghostBookshelf.Model.extend({
|
|||
return this.belongsTo('Integration');
|
||||
},
|
||||
|
||||
format(attrs) {
|
||||
return omit(attrs, 'role');
|
||||
},
|
||||
|
||||
onSaving(model, attrs, options) {
|
||||
ghostBookshelf.Model.prototype.onSaving.apply(this, arguments);
|
||||
|
||||
|
@ -52,7 +57,7 @@ const ApiKey = ghostBookshelf.Model.extend({
|
|||
// - content key = no role
|
||||
if (this.hasChanged('type') || this.hasChanged('role_id')) {
|
||||
if (this.get('type') === 'admin') {
|
||||
return Role.findOne({name: 'Admin Integration'}, Object.assign({}, options, {columns: ['id']}))
|
||||
return Role.findOne({name: attrs.role || 'Admin Integration'}, Object.assign({}, options, {columns: ['id']}))
|
||||
.then((role) => {
|
||||
this.set('role_id', role.get('id'));
|
||||
});
|
||||
|
|
|
@ -35,13 +35,14 @@ describe('Roles API', function () {
|
|||
should.exist(response);
|
||||
should.exist(response.roles);
|
||||
localUtils.API.checkResponse(response, 'roles');
|
||||
response.roles.should.have.length(6);
|
||||
response.roles.should.have.length(7);
|
||||
localUtils.API.checkResponse(response.roles[0], 'role');
|
||||
localUtils.API.checkResponse(response.roles[1], 'role');
|
||||
localUtils.API.checkResponse(response.roles[2], 'role');
|
||||
localUtils.API.checkResponse(response.roles[3], 'role');
|
||||
localUtils.API.checkResponse(response.roles[4], 'role');
|
||||
localUtils.API.checkResponse(response.roles[5], 'role');
|
||||
localUtils.API.checkResponse(response.roles[6], 'role');
|
||||
|
||||
done();
|
||||
});
|
||||
|
|
|
@ -150,19 +150,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', 64);
|
||||
result.should.have.property('done', 64);
|
||||
result.should.have.property('expected', 65);
|
||||
result.should.have.property('done', 65);
|
||||
|
||||
// Permissions & Roles
|
||||
permsAllStub.calledOnce.should.be.true();
|
||||
rolesAllStub.calledOnce.should.be.true();
|
||||
dataMethodStub.filter.callCount.should.eql(64);
|
||||
dataMethodStub.find.callCount.should.eql(5);
|
||||
baseUtilAttachStub.callCount.should.eql(64);
|
||||
dataMethodStub.filter.callCount.should.eql(65);
|
||||
dataMethodStub.find.callCount.should.eql(6);
|
||||
baseUtilAttachStub.callCount.should.eql(65);
|
||||
|
||||
fromItem.related.callCount.should.eql(64);
|
||||
fromItem.findWhere.callCount.should.eql(64);
|
||||
toItem[0].get.callCount.should.eql(128);
|
||||
fromItem.related.callCount.should.eql(65);
|
||||
fromItem.findWhere.callCount.should.eql(65);
|
||||
toItem[0].get.callCount.should.eql(130);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
|
|
|
@ -20,7 +20,7 @@ var should = require('should'),
|
|||
describe('DB version integrity', function () {
|
||||
// Only these variables should need updating
|
||||
const currentSchemaHash = 'fda0398e93a74b2dc435cb4c026679ba';
|
||||
const currentFixturesHash = '28e4057d63ce22613a2d03f520872548';
|
||||
const currentFixturesHash = '06771caf35c48fd69cb209a988237c33';
|
||||
|
||||
// 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
|
||||
|
|
Loading…
Add table
Reference in a new issue