Checks for dependencies during deletion.

This commit is contained in:
Tomas Bures 2018-09-29 20:08:49 +02:00
parent 0a08088893
commit efbfa2b366
20 changed files with 246 additions and 121 deletions

View file

@ -467,7 +467,12 @@ async function remove(context, id) {
return new interoperableErrors.InvalidStateError;
}
// FIXME - deal with deletion of dependent entities (files)
await files.removeAllTx(tx, context, 'campaign', 'file', id);
await files.removeAllTx(tx, context, 'campaign', 'attachment', id);
await tx('campaign_lists').where('campaign', id).del();
await tx('campaign_messages').where('campaign', id).del();
await tx('campaign_links').where('campaign', id).del();
await triggers.removeAllByCampaignIdTx(tx, context, id);

View file

@ -13,6 +13,7 @@ const path = require('path');
const mjml = require('mjml');
const _ = require('../lib/translate')._;
const lists = require('./lists');
const dependencyHelpers = require('../lib/dependency-helpers');
const formAllowedKeys = new Set([
'name',
@ -173,7 +174,9 @@ async function remove(context, id) {
await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'customForm', id, 'delete');
await lists.removeFormFromAllTx(tx, context, id);
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{ entityTypeId: 'list', column: 'default_form' }
]);
await tx('custom_forms_data').where('form', id).del();
await tx('custom_forms').where('id', id).del();

View file

@ -10,7 +10,9 @@ const shares = require('./shares');
const namespaceHelpers = require('../lib/namespace-helpers');
const fields = require('./fields');
const segments = require('./segments');
const imports = require('./imports');
const entitySettings = require('../lib/entity-settings');
const dependencyHelpers = require('../lib/dependency-helpers');
const UnsubscriptionMode = require('../shared/lists').UnsubscriptionMode;
@ -176,23 +178,23 @@ async function remove(context, id) {
await fields.removeAllByListIdTx(tx, context, id);
await segments.removeAllByListIdTx(tx, context, id);
await imports.removeAllByListIdTx(tx, context, id);
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{
entityTypeId: 'campaign',
query: tx => tx('campaign_lists')
.where('campaign_lists.list', id)
.innerJoin('campaigns', 'campaign_lists.campaign', 'campaigns.id')
.select(['campaigns.id', 'campaigns.name'])
}
]);
await tx('lists').where('id', id).del();
await knex.schema.dropTableIfExists('subscription__' + id);
});
}
async function removeFormFromAllTx(tx, context, formId) {
await knex.transaction(async tx => {
const entities = tx('lists').where('default_form', formId).select(['id']);
for (const entity of entities) {
await shares.enforceEntityPermissionTx(tx, context, 'list', entity.id, 'edit');
await tx('lists').where('id', entity.id).update({default_form: null});
}
});
}
async function getMergeTags(context, id) {
return await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'list', id, ['view']);
@ -224,5 +226,4 @@ module.exports.getByCid = getByCid;
module.exports.create = create;
module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck;
module.exports.remove = remove;
module.exports.removeFormFromAllTx = removeFormFromAllTx;
module.exports.getMergeTags = getMergeTags;

View file

@ -7,6 +7,8 @@ const dtHelpers = require('../lib/dt-helpers');
const interoperableErrors = require('../shared/interoperable-errors');
const namespaceHelpers = require('../lib/namespace-helpers');
const shares = require('./shares');
const files = require('./files');
const dependencyHelpers = require('../lib/dependency-helpers');
const allowedKeys = new Set(['name', 'description', 'type', 'data', 'namespace']);
@ -84,22 +86,34 @@ async function updateWithConsistencyCheck(context, entity) {
async function remove(context, id) {
await knex.transaction(async tx => {
const deps = [];
const tmpls = await tx('templates').where('type', 'mosaico').select(['id', 'name', 'data']);
for (const row of tmpls) {
const data = JSON.parse(row.data);
if (data.mosaicoTemplate === id) {
deps.push({ entityTypeId: 'template', name: row.name, link: `templates/${row.id}` });
}
}
if (deps.length > 0) {
throw new interoperableErrors.DependencyPresentError('', {
dependencies: deps
});
}
await shares.enforceEntityPermissionTx(tx, context, 'mosaicoTemplate', id, 'delete');
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{
entityTypeId: 'template',
rows: async (tx, limit) => {
const result = [];
const tmpls = await tx('templates').where('type', 'mosaico').select(['id', 'name', 'data']);
for (const tmpl of tmpls) {
const data = JSON.parse(tmpl.data);
if (data.mosaicoTemplate === id) {
result.push(tmpl);
}
limit -= 1;
if (limit <= 0) break;
}
return result;
}
}
]);
await files.removeAllTx(tx, context, 'mosaicoTemplate', 'file', id);
await files.removeAllTx(tx, context, 'mosaicoTemplate', 'block', id);
await tx('mosaico_templates').where('id', id).del();
});
}

View file

@ -7,6 +7,7 @@ const interoperableErrors = require('../shared/interoperable-errors');
const shares = require('./shares');
const entitySettings = require('../lib/entity-settings');
const namespaceHelpers = require('../lib/namespace-helpers');
const dependencyHelpers = require('../lib/dependency-helpers');
const allowedKeys = new Set(['name', 'description', 'namespace']);
@ -176,12 +177,8 @@ async function remove(context, id) {
await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'namespace', id, 'delete');
const childNs = await tx('namespaces').where('namespace', id).first();
if (childNs) {
throw new interoperableErrors.ChildDetectedError();
}
// FIXME - Remove all contained entities first
const entityTypesWithNamespace = Object.keys(entitySettings.getEntityTypes());
await dependencyHelpers.ensureNoDependencies(tx, context, id, entityTypesWithNamespace.map(entityTypeId => ({ entityTypeId: entityTypeId, column: 'namespace' })));
await tx('namespaces').where('id', id).del();
});

View file

@ -8,6 +8,7 @@ const interoperableErrors = require('../shared/interoperable-errors');
const namespaceHelpers = require('../lib/namespace-helpers');
const shares = require('./shares');
const reports = require('./reports');
const dependencyHelpers = require('../lib/dependency-helpers');
const allowedKeys = new Set(['name', 'description', 'mime_type', 'user_fields', 'js', 'hbs', 'namespace']);
@ -77,7 +78,9 @@ async function remove(context, id) {
await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'reportTemplate', id, 'delete');
await reports.removeAllByReportTemplateIdTx(tx, context, id);
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{ entityTypeId: 'report', column: 'report_template' }
]);
await tx('report_templates').where('id', id).del();
});

View file

@ -8,6 +8,8 @@ const interoperableErrors = require('../shared/interoperable-errors');
const fields = require('./fields');
const namespaceHelpers = require('../lib/namespace-helpers');
const shares = require('./shares');
const reportHelpers = require('../lib/report-helpers');
const fs = require('fs-extra-promise');
const ReportState = require('../shared/reports').ReportState;
@ -114,9 +116,12 @@ async function updateWithConsistencyCheck(context, entity) {
async function removeTx(tx, context, id) {
await shares.enforceEntityPermissionTx(tx, context, 'report', id, 'delete');
await tx('reports').where('id', id).del();
const report = tx('reports').where('id', id).first();
// FIXME: Remove generated files
await fs.removeAsync(reportHelpers.getReportContentFile(report));
await fs.removeAsync(reportHelpers.getReportOutputFile(report));
await tx('reports').where('id', id).del();
}
async function remove(context, id) {
@ -125,14 +130,6 @@ async function remove(context, id) {
});
}
async function removeAllByReportTemplateIdTx(tx, context, templateId) {
const entities = await tx('reports').where('report_template', templateId).select(['id']);
for (const entity of entities) {
await removeTx(tx, context, entity.id);
}
}
async function updateFields(id, fields) {
return await knex('reports').where('id', id).update(fields);
}
@ -203,7 +200,6 @@ module.exports.listDTAjax = listDTAjax;
module.exports.create = create;
module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck;
module.exports.remove = remove;
module.exports.removeAllByReportTemplateIdTx = removeAllByReportTemplateIdTx;
module.exports.updateFields = updateFields;
module.exports.listByState = listByState;
module.exports.bulkChangeState = bulkChangeState;

View file

@ -9,6 +9,7 @@ const hasher = require('node-object-hash')();
const moment = require('moment');
const fields = require('./fields');
const subscriptions = require('./subscriptions');
const dependencyHelpers = require('../lib/dependency-helpers');
const { parseDate, parseBirthday, DateFormat } = require('../shared/date');
@ -331,7 +332,15 @@ async function updateWithConsistencyCheck(context, listId, entity) {
async function removeTx(tx, context, listId, id) {
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageSegments');
// FIXME - check dependencies: campaigns
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{
entityTypeId: 'campaign',
query: tx => tx('campaign_lists')
.where('campaign_lists.segment', id)
.innerJoin('campaigns', 'campaign_lists.campaign', 'campaigns.id')
.select(['campaigns.id', 'campaigns.name'])
}
]);
// The listId "where" is here to prevent deleting segment of a list for which a user does not have permission
await tx('segments').where({list: listId, id}).del();

View file

@ -12,6 +12,7 @@ const {MailerType, getSystemSendConfigurationId} = require('../shared/send-confi
const contextHelpers = require('../lib/context-helpers');
const mailers = require('../lib/mailers');
const senders = require('../lib/senders');
const dependencyHelpers = require('../lib/dependency-helpers');
const allowedKeys = new Set(['name', 'description', 'from_email', 'from_email_overridable', 'from_name', 'from_name_overridable', 'reply_to', 'reply_to_overridable', 'subject', 'subject_overridable', 'x_mailer', 'verp_hostname', 'mailer_type', 'mailer_settings', 'namespace']);
@ -147,9 +148,11 @@ async function remove(context, id) {
await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'sendConfiguration', id, 'delete');
await tx('lists').update({send_configuration: null}).where('send_configuration', id);
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{ entityTypeId: 'campaign', column: 'send_configuration' },
{ entityTypeId: 'list', column: 'send_configuration' }
]);
// If any campaign with the send configuration exists, this fails due to sql foreign key
await tx('send_configurations').where('id', id).del();
});
}

View file

@ -140,7 +140,7 @@ async function rebuildPermissionsTx(tx, restriction) {
[restriction.entityTypeId]: entityType
};
} else {
restrictedEntityTypes = entitySettings.getEntityTypes();
restrictedEntityTypes = entitySettings.getEntityTypesWithPermissions();
}
@ -375,7 +375,7 @@ async function regenerateRoleNamesTable() {
await knex.transaction(async tx => {
await tx('generated_role_names').del();
const entityTypeIds = ['global', ...Object.keys(entitySettings.getEntityTypes())];
const entityTypeIds = ['global', ...Object.keys(entitySettings.getEntityTypesWithPermissions())];
for (const entityTypeId of entityTypeIds) {
const roles = config.roles[entityTypeId];
@ -539,6 +539,14 @@ async function checkEntityPermission(context, entityTypeId, entityId, requiredOp
});
}
async function checkEntityPermissionTx(tx, context, entityTypeId, entityId, requiredOperations) {
if (!entityId) {
return false;
}
return await _checkPermissionTx(tx, context, entityTypeId, entityId, requiredOperations);
}
async function checkTypePermission(context, entityTypeId, requiredOperations) {
return await knex.transaction(async tx => {
return await _checkPermissionTx(tx, context, entityTypeId, null, requiredOperations);
@ -626,6 +634,7 @@ module.exports.enforceEntityPermission = enforceEntityPermission;
module.exports.enforceEntityPermissionTx = enforceEntityPermissionTx;
module.exports.enforceTypePermission = enforceTypePermission;
module.exports.enforceTypePermissionTx = enforceTypePermissionTx;
module.exports.checkEntityPermissionTx = checkEntityPermissionTx;
module.exports.checkEntityPermission = checkEntityPermission;
module.exports.checkTypePermission = checkTypePermission;
module.exports.enforceGlobalPermission = enforceGlobalPermission;

View file

@ -9,6 +9,7 @@ const namespaceHelpers = require('../lib/namespace-helpers');
const shares = require('./shares');
const reports = require('./reports');
const files = require('./files');
const dependencyHelpers = require('../lib/dependency-helpers');
const allowedKeys = new Set(['name', 'description', 'type', 'data', 'html', 'text', 'namespace']);
@ -97,17 +98,15 @@ async function remove(context, id) {
await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'template', id, 'delete');
const depCampaigns = await tx('template_dep_campaigns')
.where('template_dep_campaigns.template', id)
.innerJoin('campaigns', 'template_dep_campaigns.campaign', 'campaigns.id')
.limit(interoperableErrors.defaultNoOfDependenciesReported)
.select(['campaigns.id', 'campaigns.name']);
if (depCampaigns.length > 0) {
throw new interoperableErrors.DependencyPresentError('', {
dependencies: depCampaigns.map(row => ({ entityTypeId: 'campaign', name: row.name, link: `campaigns/${row.id}` }))
});
}
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{
entityTypeId: 'campaign',
query: tx => tx('template_dep_campaigns')
.where('template_dep_campaigns.template', id)
.innerJoin('campaigns', 'template_dep_campaigns.campaign', 'campaigns.id')
.select(['campaigns.id', 'campaigns.name'])
}
]);
await files.removeAllTx(tx, context, 'template', 'file', id);