mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-21 01:41:46 +03:00
250 lines
7.5 KiB
JavaScript
250 lines
7.5 KiB
JavaScript
|
const ObjectId = require('bson-objectid').default;
|
||
|
const logging = require('../../../shared/logging');
|
||
|
|
||
|
const MIGRATION_USER = 1;
|
||
|
|
||
|
/**
|
||
|
* Creates a migration which will add a permission to the database
|
||
|
*
|
||
|
* @param {Object} config
|
||
|
* @param {string} config.name - The name of the permission
|
||
|
* @param {string} config.action - The action_type of the permission
|
||
|
* @param {string} config.object - The object_type of the permission
|
||
|
*
|
||
|
* @returns {Migration}
|
||
|
*/
|
||
|
function addPermission(config) {
|
||
|
return createTransactionalMigration(
|
||
|
async function up(connection) {
|
||
|
const existingPermission = await connection('permissions').where({
|
||
|
name: config.name,
|
||
|
action_type: config.action,
|
||
|
object_type: config.object
|
||
|
}).first();
|
||
|
|
||
|
if (existingPermission) {
|
||
|
logging.warn(`Permission for ${config.action}:${config.object} already added`);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
logging.info(`Adding permission for ${config.action}:${config.object}`);
|
||
|
|
||
|
const date = connection.raw('CURRENT_TIMESTAMP');
|
||
|
|
||
|
await connection('permissions').insert({
|
||
|
id: ObjectId.generate(),
|
||
|
name: config.name,
|
||
|
action_type: config.action,
|
||
|
object_type: config.object,
|
||
|
created_at: date,
|
||
|
created_by: MIGRATION_USER,
|
||
|
updated_at: date,
|
||
|
updated_by: MIGRATION_USER
|
||
|
});
|
||
|
},
|
||
|
async function down(connection) {
|
||
|
const existingPermission = await connection('permissions').where({
|
||
|
name: config.name,
|
||
|
action_type: config.action,
|
||
|
object_type: config.object
|
||
|
}).first();
|
||
|
|
||
|
if (!existingPermission) {
|
||
|
logging.warn(`Permission for ${config.action}:${config.object} already removed`);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
logging.info(`Removing permission for ${config.action}:${config.object}`);
|
||
|
|
||
|
await connection('permissions').where({
|
||
|
action_type: config.action,
|
||
|
object_type: config.object
|
||
|
}).del();
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a migration which will link a permission to a role in the database
|
||
|
*
|
||
|
* @param {Object} config
|
||
|
* @param {string} config.permission - The name of the permission
|
||
|
* @param {string} config.role - The name of the role
|
||
|
*
|
||
|
* @returns {Migration}
|
||
|
*/
|
||
|
function addPermissionToRole(config) {
|
||
|
return createTransactionalMigration(
|
||
|
async function up(connection) {
|
||
|
const permission = await connection('permissions').where({
|
||
|
name: config.permission
|
||
|
}).first();
|
||
|
|
||
|
if (!permission) {
|
||
|
throw new Error(
|
||
|
`Cannot add permission(${config.permission}) to role(${config.role}) - permission does not exist`
|
||
|
);
|
||
|
}
|
||
|
|
||
|
const role = await connection('roles').where({
|
||
|
name: config.role
|
||
|
}).first();
|
||
|
|
||
|
if (!role) {
|
||
|
throw new Error(
|
||
|
`Cannot add permission(${config.permission}) to role(${config.role}) - role does not exist`
|
||
|
);
|
||
|
}
|
||
|
|
||
|
const existingRelation = await connection('permissions_roles').where({
|
||
|
permission_id: permission.id,
|
||
|
role_id: role.id
|
||
|
}).first();
|
||
|
|
||
|
if (existingRelation) {
|
||
|
logging.warn(`Adding permission(${config.permission}) to role(${config.role}) - already exists`);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
logging.warn(`Adding permission(${config.permission}) to role(${config.role})`);
|
||
|
await connection('permissions_roles').insert({
|
||
|
id: ObjectId.generate(),
|
||
|
permission_id: permission.id,
|
||
|
role_id: role.id
|
||
|
});
|
||
|
},
|
||
|
async function down(connection) {
|
||
|
const permission = await connection('permissions').where({
|
||
|
name: config.permission
|
||
|
}).first();
|
||
|
|
||
|
if (!permission) {
|
||
|
throw new Error(
|
||
|
`Cannot remove permission(${config.permission}) from role(${config.role}) - permission does not exist`
|
||
|
);
|
||
|
}
|
||
|
|
||
|
const role = await connection('roles').where({
|
||
|
name: config.role
|
||
|
}).first();
|
||
|
|
||
|
if (!role) {
|
||
|
throw new Error(
|
||
|
`Cannot remove permission(${config.permission}) from role(${config.role}) - role does not exist`
|
||
|
);
|
||
|
}
|
||
|
|
||
|
const existingRelation = await connection('permissions_roles').where({
|
||
|
permission_id: permission.id,
|
||
|
role_id: role.id
|
||
|
}).first();
|
||
|
|
||
|
if (!existingRelation) {
|
||
|
logging.warn(`Removing permission(${config.permission}) from role(${config.role}) - already removed`);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
logging.info(`Removing permission(${config.permission}) from role(${config.role})`);
|
||
|
await connection('permissions_roles').where({
|
||
|
permission_id: permission.id,
|
||
|
role_id: role.id
|
||
|
}).del();
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a migration which will add a permission to the database, and then link it to roles
|
||
|
*
|
||
|
* @param {Object} config
|
||
|
* @param {string} config.name - The name of the permission
|
||
|
* @param {string} config.action - The action_type of the permission
|
||
|
* @param {string} config.object - The object_type of the permission
|
||
|
*
|
||
|
* @param {string[]} roles - A list of role names
|
||
|
*
|
||
|
* @returns {Migration}
|
||
|
*/
|
||
|
function addPermissionWithRoles(config, roles) {
|
||
|
return combineTransactionalMigrations(
|
||
|
addPermission(config),
|
||
|
...roles.map((role => addPermissionToRole({permission: config.name, role})))
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param {(connection: import('knex')) => Promise<void>} up
|
||
|
* @param {(connection: import('knex')) => Promise<void>} down
|
||
|
*
|
||
|
* @returns {Migration}
|
||
|
*/
|
||
|
function createTransactionalMigration(up, down) {
|
||
|
return {
|
||
|
config: {
|
||
|
transaction: true
|
||
|
},
|
||
|
async up(config) {
|
||
|
await up(config.transacting);
|
||
|
},
|
||
|
async down(config) {
|
||
|
await down(config.transacting);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param {Migration[]} migrations
|
||
|
*
|
||
|
* @returns {Migration}
|
||
|
*/
|
||
|
function combineTransactionalMigrations(...migrations) {
|
||
|
return {
|
||
|
config: {
|
||
|
transaction: true
|
||
|
},
|
||
|
async up(config) {
|
||
|
for (const migration of migrations) {
|
||
|
await migration.up(config);
|
||
|
}
|
||
|
},
|
||
|
async down(config) {
|
||
|
// Down migrations must be run backwards!!
|
||
|
const reverseMigrations = migrations.slice().reverse();
|
||
|
for (const migration of reverseMigrations) {
|
||
|
await migration.down(config);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
module.exports = {
|
||
|
addPermission,
|
||
|
addPermissionToRole,
|
||
|
addPermissionWithRoles,
|
||
|
createTransactionalMigration,
|
||
|
combineTransactionalMigrations,
|
||
|
meta: {
|
||
|
MIGRATION_USER
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @typedef {Object} TransactionalMigrationFunctionOptions
|
||
|
*
|
||
|
* @prop {import('knex')} transacting
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @typedef {(options: TransactionalMigrationFunctionOptions) => Promise<void>} TransactionalMigrationFunction
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @typedef {Object} Migration
|
||
|
*
|
||
|
* @prop {Object} config
|
||
|
* @prop {boolean} config.transaction
|
||
|
*
|
||
|
* @prop {TransactionalMigrationFunction} up
|
||
|
* @prop {TransactionalMigrationFunction} down
|
||
|
*/
|