mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-30 06:12:03 +03:00
ec119c458c
no-issue These utils are the first steps toward getting the models out of our migrations! The utils here interact directly with the database and where possible don't reinvent the wheel, by using smaller building blocks to build more comples ones.
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
|
|
*/
|