2205 - Prevent relations to being logged in
gitea/salix/pipeline/head This commit has test failures Details

This commit is contained in:
Joan Sanchez 2020-05-26 14:37:34 +02:00
parent 731e411cf8
commit d24f04912e
4 changed files with 92 additions and 42 deletions

View File

@ -0,0 +1 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('WorkerLog', '*', 'READ', 'ALLOW', 'ROLE', 'hr');

View File

@ -2,6 +2,7 @@ const pick = require('object.pick');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
module.exports = function(Self) { module.exports = function(Self) {
// const relations = ctx.Model.relations;
Self.setup = function() { Self.setup = function() {
Self.super_.setup.call(this); Self.super_.setup.call(this);
}; };
@ -12,23 +13,33 @@ module.exports = function(Self) {
}); });
Self.observe('before save', async function(ctx) { Self.observe('before save', async function(ctx) {
let options = {}; const appModels = ctx.Model.app.models;
const options = {};
// Check for transactions
if (ctx.options && ctx.options.transaction) if (ctx.options && ctx.options.transaction)
options.transaction = ctx.options.transaction; options.transaction = ctx.options.transaction;
let oldInstance; let oldInstance;
let oldInstanceFk;
let newInstance; let newInstance;
if (ctx.data) { if (ctx.data) {
oldInstanceFk = pick(ctx.currentInstance, Object.keys(ctx.data)); const changes = pick(ctx.currentInstance, Object.keys(ctx.data));
newInstance = await fkToValue(ctx.data, ctx); newInstance = await fkToValue(ctx.data, ctx);
oldInstance = await fkToValue(oldInstanceFk, ctx); oldInstance = await fkToValue(changes, ctx);
if (ctx.where && !ctx.currentInstance) { if (ctx.where && !ctx.currentInstance) {
let fields = Object.keys(ctx.data); const fields = Object.keys(ctx.data);
ctx.oldInstances = await ctx.Model.app.models[ctx.Model.definition.name].find({where: ctx.where, fields: fields}, options); const modelName = modelDef.name;
ctx.oldInstances = await appModels[modelName].find({
where: ctx.where,
fields: fields
}, options);
} }
} }
// Get changes from created instance
if (ctx.isNewInstance) if (ctx.isNewInstance)
newInstance = await fkToValue(ctx.instance.__data, ctx); newInstance = await fkToValue(ctx.instance.__data, ctx);
@ -37,18 +48,24 @@ module.exports = function(Self) {
}); });
Self.observe('before delete', async function(ctx) { Self.observe('before delete', async function(ctx) {
const appModels = ctx.Model.app.models;
const definition = ctx.Model.definition;
const relations = ctx.Model.relations;
let options = {}; let options = {};
if (ctx.options && ctx.options.transaction) if (ctx.options && ctx.options.transaction)
options.transaction = ctx.options.transaction; options.transaction = ctx.options.transaction;
if (ctx.where) { if (ctx.where) {
let affectedModel = ctx.Model.definition.name; let affectedModel = definition.name;
let definition = ctx.Model.definition; let deletedInstances = await appModels[affectedModel].find({
let deletedInstances = await ctx.Model.app.models[affectedModel].find({where: ctx.where}, options); where: ctx.where
}, options);
let relation = definition.settings.log.relation; let relation = definition.settings.log.relation;
if (relation) { if (relation) {
let primaryKey = ctx.Model.relations[relation].keyFrom; let primaryKey = relations[relation].keyFrom;
let arrangedDeletedInstances = []; let arrangedDeletedInstances = [];
for (let i = 0; i < deletedInstances.length; i++) { for (let i = 0; i < deletedInstances.length; i++) {
@ -69,6 +86,8 @@ module.exports = function(Self) {
}); });
async function logDeletedInstances(ctx, loopBackContext) { async function logDeletedInstances(ctx, loopBackContext) {
const appModels = ctx.Model.app.models;
const modelDef = ctx.Model.definition;
let options = {}; let options = {};
if (ctx.options && ctx.options.transaction) if (ctx.options && ctx.options.transaction)
options.transaction = ctx.options.transaction; options.transaction = ctx.options.transaction;
@ -78,14 +97,14 @@ module.exports = function(Self) {
if (loopBackContext) if (loopBackContext)
userFk = loopBackContext.active.accessToken.userId; userFk = loopBackContext.active.accessToken.userId;
let definition = ctx.Model.definition; let definition = modelDef;
let changedModelValue = definition.settings.log.changedModelValue; let changedModelValue = definition.settings.log.changedModelValue;
let logRecord = { let logRecord = {
originFk: instance.originFk, originFk: instance.originFk,
userFk: userFk, userFk: userFk,
action: 'delete', action: 'delete',
changedModel: ctx.Model.definition.name, changedModel: modelDef.name,
changedModelId: instance.id, changedModelId: instance.id,
changedModelValue: instance[changedModelValue], changedModelValue: instance[changedModelValue],
oldInstance: instance, oldInstance: instance,
@ -95,26 +114,44 @@ module.exports = function(Self) {
delete instance.originFk; delete instance.originFk;
let logModel = definition.settings.log.model; let logModel = definition.settings.log.model;
await ctx.Model.app.models[logModel].create(logRecord, options); await appModels[logModel].create(logRecord, options);
}); });
} }
// Get log values from a foreign key
async function fkToValue(instance, ctx) { async function fkToValue(instance, ctx) {
const appModels = ctx.Model.app.models;
const relations = ctx.Model.relations;
let options = {}; let options = {};
// Check for transactions
if (ctx.options && ctx.options.transaction) if (ctx.options && ctx.options.transaction)
options.transaction = ctx.options.transaction; options.transaction = ctx.options.transaction;
let cleanInstance = JSON.parse(JSON.stringify(instance)); const instanceCopy = JSON.parse(JSON.stringify(instance));
let result = {}; const result = {};
for (let key in cleanInstance) { for (const key in instanceCopy) {
let val = cleanInstance[key]; let value = instanceCopy[key];
if (val === undefined || val === null) continue;
for (let key1 in ctx.Model.relations) { if (value instanceof Object)
let val1 = ctx.Model.relations[key1]; continue;
if (val1.keyFrom == key && key != 'id') {
let recordSet = await ctx.Model.app.models[val1.modelTo.modelName].findById(val, null, options); if (value === undefined || value === null) continue;
for (let relationName in relations) {
const relation = relations[relationName];
if (relation.keyFrom == key && key != 'id') {
const model = relation.modelTo;
const modelName = relation.modelTo.modelName;
const properties = model && model.definition.properties;
const settings = model && model.definition.settings;
const recordSet = await appModels[modelName].findById(value, null, options);
const hasShowField = settings.log && settings.log.showField;
let showField = hasShowField && recordSet
&& recordSet[settings.log.showField];
let showField = val1.modelTo && val1.modelTo.definition.settings.log && val1.modelTo.definition.settings.log.showField && recordSet && recordSet[val1.modelTo.definition.settings.log.showField];
if (!showField) { if (!showField) {
const showFieldNames = [ const showFieldNames = [
'name', 'name',
@ -122,7 +159,10 @@ module.exports = function(Self) {
'code' 'code'
]; ];
for (field of showFieldNames) { for (field of showFieldNames) {
if (val1.modelTo.definition.properties && val1.modelTo.definition.properties[field] && recordSet && recordSet[field]) { const propField = properties && properties[field];
const recordField = recordSet && recordSet[field];
if (propField && recordField) {
showField = field; showField = field;
break; break;
} }
@ -130,25 +170,29 @@ module.exports = function(Self) {
} }
if (showField && recordSet && recordSet[showField]) { if (showField && recordSet && recordSet[showField]) {
val = recordSet[showField]; value = recordSet[showField];
break; break;
} }
val = recordSet && recordSet.id || val; value = recordSet && recordSet.id || value;
break; break;
} }
} }
result[key] = val; result[key] = value;
} }
return result; return result;
} }
async function logInModel(ctx, loopBackContext) { async function logInModel(ctx, loopBackContext) {
let options = {}; const appModels = ctx.Model.app.models;
const definition = ctx.Model.definition;
const defSettings = ctx.Model.definition.settings;
const relations = ctx.Model.relations;
const options = {};
if (ctx.options && ctx.options.transaction) if (ctx.options && ctx.options.transaction)
options.transaction = ctx.options.transaction; options.transaction = ctx.options.transaction;
let definition = ctx.Model.definition;
let primaryKey; let primaryKey;
for (let property in definition.properties) { for (let property in definition.properties) {
if (definition.properties[property].id) { if (definition.properties[property].id) {
@ -163,11 +207,11 @@ module.exports = function(Self) {
// RELATIONS LOG // RELATIONS LOG
let changedModelId; let changedModelId;
if (ctx.instance && !definition.settings.log.relation) { if (ctx.instance && !defSettings.log.relation) {
originId = ctx.instance.id; originId = ctx.instance.id;
changedModelId = ctx.instance.id; changedModelId = ctx.instance.id;
} else if (definition.settings.log.relation) { } else if (defSettings.log.relation) {
primaryKey = ctx.Model.relations[definition.settings.log.relation].keyFrom; primaryKey = relations[defSettings.log.relation].keyFrom;
if (ctx.where && ctx.where[primaryKey]) if (ctx.where && ctx.where[primaryKey])
originId = ctx.where[primaryKey]; originId = ctx.where[primaryKey];
@ -181,12 +225,16 @@ module.exports = function(Self) {
} }
// Sets the changedModelValue to save and the instances changed in case its an updateAll // Sets the changedModelValue to save and the instances changed in case its an updateAll
let showField = definition.settings.log.showField; let showField = defSettings.log.showField;
let where; let where;
if (showField && (!ctx.instance || !ctx.instance[showField]) && ctx.where) { if (showField && (!ctx.instance || !ctx.instance[showField]) && ctx.where) {
changedModelId = []; changedModelId = [];
where = []; where = [];
let changedInstances = await ctx.Model.app.models[definition.name].find({where: ctx.where, fields: ['id', showField, primaryKey]}, options); let changedInstances = await appModels[definition.name].find({
where: ctx.where,
fields: ['id', showField, primaryKey]
}, options);
changedInstances.forEach(element => { changedInstances.forEach(element => {
where.push(element[showField]); where.push(element[showField]);
changedModelId.push(element.id); changedModelId.push(element.id);
@ -195,7 +243,6 @@ module.exports = function(Self) {
} else if (ctx.hookState.oldInstance) } else if (ctx.hookState.oldInstance)
where = ctx.instance[showField]; where = ctx.instance[showField];
// Set oldInstance, newInstance, userFk and action // Set oldInstance, newInstance, userFk and action
let oldInstance = {}; let oldInstance = {};
if (ctx.hookState.oldInstance) if (ctx.hookState.oldInstance)
@ -211,14 +258,14 @@ module.exports = function(Self) {
let action = setActionType(ctx); let action = setActionType(ctx);
removeUnloggableProperties(definition, oldInstance); removeUnloggable(definition, oldInstance);
removeUnloggableProperties(definition, newInstance); removeUnloggable(definition, newInstance);
let logRecord = { let logRecord = {
originFk: originId, originFk: originId,
userFk: userFk, userFk: userFk,
action: action, action: action,
changedModel: ctx.Model.definition.name, changedModel: definition.name,
changedModelId: changedModelId, // Model property with an different data type will throw a NaN error changedModelId: changedModelId, // Model property with an different data type will throw a NaN error
changedModelValue: where, changedModelValue: where,
oldInstance: oldInstance, oldInstance: oldInstance,
@ -226,9 +273,9 @@ module.exports = function(Self) {
}; };
let logsToSave = setLogsToSave(where, changedModelId, logRecord, ctx); let logsToSave = setLogsToSave(where, changedModelId, logRecord, ctx);
let logModel = definition.settings.log.model; let logModel = defSettings.log.model;
await ctx.Model.app.models[logModel].create(logsToSave, options); await appModels[logModel].create(logsToSave, options);
} }
/** /**
@ -236,7 +283,7 @@ module.exports = function(Self) {
* @param {*} definition Model definition * @param {*} definition Model definition
* @param {*} properties Modified object properties * @param {*} properties Modified object properties
*/ */
function removeUnloggableProperties(definition, properties) { function removeUnloggable(definition, properties) {
const propList = Object.keys(properties); const propList = Object.keys(properties);
const propDefs = new Map(); const propDefs = new Map();

View File

@ -1,3 +1,4 @@
Date: Fecha
Model: Modelo Model: Modelo
Action: Acción Action: Acción
Author: Autor Author: Autor

View File

@ -62,7 +62,8 @@
"url" : "/log", "url" : "/log",
"state": "worker.card.workerLog", "state": "worker.card.workerLog",
"component": "vn-worker-log", "component": "vn-worker-log",
"description": "Log" "description": "Log",
"acl": ["hr"]
}, { }, {
"url": "/pbx", "url": "/pbx",
"state": "worker.card.pbx", "state": "worker.card.pbx",