diff --git a/db/versions/11388-crimsonFern/00-firstScript.sql b/db/versions/11388-crimsonFern/00-firstScript.sql new file mode 100644 index 000000000..8f48ae0d8 --- /dev/null +++ b/db/versions/11388-crimsonFern/00-firstScript.sql @@ -0,0 +1,2 @@ +REVOKE UPDATE (packingOut) ON vn.item FROM employee; +GRANT UPDATE (packingOut) ON vn.item TO buyerBoss; diff --git a/loopback/common/methods/application/checkColumnPermission.js b/loopback/common/methods/application/checkColumnPermission.js new file mode 100644 index 000000000..2f5611136 --- /dev/null +++ b/loopback/common/methods/application/checkColumnPermission.js @@ -0,0 +1,62 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethod('checkColumnPermission', { + description: 'Check if the user has permission for a specific column', + accessType: 'EXECUTE', + accepts: [{ + arg: 'schema', + type: 'string', + description: 'The schema of db', + required: true, + }, { + arg: 'table', + type: 'string', + description: 'The table of schema', + required: true, + }, { + arg: 'column', + type: 'string', + description: 'The column of table', + required: true, + }, { + arg: 'privilegeType', + type: 'string', + description: 'Privilege type (SELECT|UPDATE|INSERT|DELETE)', + required: true, + }, { + arg: 'userId', + type: 'number', + description: 'The user id', + required: true, + } + ], + returns: { + type: 'any', + root: true + }, + http: { + path: `/check-column-permission`, + verb: 'GET' + } + }); + + Self.checkColumnPermission = async(schema, table, column, privilegeType, userId) => { + const models = Self.app.models; + const user = await models.VnUser.findById(userId); + if (!user) return; + const role = await models.VnRole.findById(user.roleFk); + const permissions = await Self.rawSql(` + SELECT TRUE + FROM information_schema.COLUMN_PRIVILEGES + WHERE TABLE_SCHEMA = ? + AND TABLE_NAME = ? + AND COLUMN_NAME = ? + AND PRIVILEGE_TYPE = ? + AND REGEXP_SUBSTR(GRANTEE, '[a-zA-Z]+') = ? + `, [schema, table, column, privilegeType, role.name]); + + if (!permissions.length) + throw new UserError(`You do not have sufficient privileges to modify a specific column`); + }; +}; diff --git a/loopback/common/methods/application/spec/checkColumnPermission.spec.js b/loopback/common/methods/application/spec/checkColumnPermission.spec.js new file mode 100644 index 000000000..2987d059b --- /dev/null +++ b/loopback/common/methods/application/spec/checkColumnPermission.spec.js @@ -0,0 +1,74 @@ +const {models} = require('vn-loopback/server/server'); +const UserError = require('vn-loopback/util/user-error'); + +describe('Application checkColumnPermission()', () => { + let tx; + let options; + beforeEach(async() => { + tx = await models.Application.beginTransaction({}); + options = {transaction: tx}; + + await models.Application.rawSql(` + CREATE TABLE vn.testTable ( + testColumn VARCHAR(255) + ) ENGINE=InnoDB; + `, null, options); + + const user = await models.VnUser.findById(1, null, options); + await user.updateAttributes({ + roleFk: 1, + }, options); + + await models.Application.rawSql(` + GRANT UPDATE (testColumn) ON vn.testTable TO employee; + `, null, options); + }); + + afterEach(async() => { + await models.Application.rawSql(` + DROP TABLE vn.testTable; + `); // Non-transactional DDL operations + await tx.rollback(); + }); + + it('should pass if the user has the required permission', async() => { + const response = await models.Application.checkColumnPermission( + 'vn', + 'testTable', + 'testColumn', + 'UPDATE', + 1 + ); + + expect(response).toBeUndefined(); + }); + + it('should throw an error if the user lacks permission', async() => { + try { + const result = await models.Application.checkColumnPermission( + 'vn', + 'testTable', + 'testColumn', + 'INSERT', + 1 + ); + + expect(result).toBeUndefined(); + } catch (err) { + expect(err).toBeInstanceOf(UserError); + expect(err.message).toBeDefined(); + } + }); + + it('should not throw an error if the user does not exist', async() => { + const response = await models.Application.checkColumnPermission( + 'vn', + 'testTable', + 'testColumn', + 'UPDATE', + 999999 // Non-existent user + ); + + expect(response).toBeUndefined(); + }); +}); diff --git a/loopback/common/models/application.js b/loopback/common/models/application.js index 80c58ddc1..725c332d4 100644 --- a/loopback/common/models/application.js +++ b/loopback/common/models/application.js @@ -5,4 +5,5 @@ module.exports = function(Self) { require('../methods/application/executeProc')(Self); require('../methods/application/executeFunc')(Self); require('../methods/application/getEnumValues')(Self); + require('../methods/application/checkColumnPermission')(Self); }; diff --git a/loopback/common/models/vn-model.js b/loopback/common/models/vn-model.js index e24653d13..7444a5562 100644 --- a/loopback/common/models/vn-model.js +++ b/loopback/common/models/vn-model.js @@ -28,6 +28,8 @@ module.exports = function(Self) { }); this.beforeRemote('**', async ctx => { + // Aquí va el código, pero... ¿Como identificar los cambios y si es INSERT, UPDATE O DELETE? + if (!this.hasFilter(ctx)) return; const defaultLimit = this.app.orm.selectLimit; diff --git a/loopback/locale/es.json b/loopback/locale/es.json index abd2f79a0..76fedf23f 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -394,6 +394,7 @@ "Holidays to past days not available": "Las vacaciones a días pasados no están disponibles", "All tickets have a route order": "Todos los tickets tienen orden de ruta", "There are tickets to be invoiced": "La zona tiene tickets por facturar", + "You do not have sufficient privileges to modify a specific column": "No tienes suficientes permisos para modificar una columna específica", "Incorrect delivery order alert on route": "Alerta de orden de entrega incorrecta en ruta: {{ route }} zona: {{ zone }}", "Ticket has been delivered out of order": "El ticket {{ticket}} {{{fullUrl}}} no ha sigo entregado en su orden.", "Price cannot be blank": "El precio no puede estar en blanco" diff --git a/modules/item/back/models/item.js b/modules/item/back/models/item.js index 44a51594c..7f9c121d5 100644 --- a/modules/item/back/models/item.js +++ b/modules/item/back/models/item.js @@ -1,4 +1,5 @@ let UserError = require('vn-loopback/util/user-error'); +const models = require('vn-loopback/server/server').models; module.exports = Self => { require('../methods/item/filter')(Self); @@ -22,6 +23,11 @@ module.exports = Self => { Self.observe('before save', async function(ctx) { await Self.availableId(ctx); + if (!(ctx?.data?.packingOut === undefined)) { + await models.Application.checkColumnPermission( + 'vn', 'item', 'packingOut', 'UPDATE', ctx.options.accessToken.userId + ); + } }); Self.availableId = async function(ctx) {