feat: refs #8239 Added checkColumnPermission method #3317
|
@ -0,0 +1,2 @@
|
||||||
|
REVOKE UPDATE (packingOut) ON vn.item FROM employee;
|
||||||
|
GRANT UPDATE (packingOut) ON vn.item TO buyerBoss;
|
|
@ -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`);
|
||||||
|
};
|
||||||
|
};
|
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
|
@ -5,4 +5,5 @@ module.exports = function(Self) {
|
||||||
require('../methods/application/executeProc')(Self);
|
require('../methods/application/executeProc')(Self);
|
||||||
require('../methods/application/executeFunc')(Self);
|
require('../methods/application/executeFunc')(Self);
|
||||||
require('../methods/application/getEnumValues')(Self);
|
require('../methods/application/getEnumValues')(Self);
|
||||||
|
require('../methods/application/checkColumnPermission')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,8 @@ module.exports = function(Self) {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.beforeRemote('**', async ctx => {
|
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;
|
if (!this.hasFilter(ctx)) return;
|
||||||
|
|
||||||
const defaultLimit = this.app.orm.selectLimit;
|
const defaultLimit = this.app.orm.selectLimit;
|
||||||
|
|
|
@ -394,6 +394,7 @@
|
||||||
"Holidays to past days not available": "Las vacaciones a días pasados no están disponibles",
|
"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",
|
"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",
|
"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 }}",
|
"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.",
|
"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"
|
"Price cannot be blank": "El precio no puede estar en blanco"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
let UserError = require('vn-loopback/util/user-error');
|
let UserError = require('vn-loopback/util/user-error');
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/item/filter')(Self);
|
require('../methods/item/filter')(Self);
|
||||||
|
@ -22,6 +23,11 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
Self.observe('before save', async function(ctx) {
|
||||||
await Self.availableId(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) {
|
Self.availableId = async function(ctx) {
|
||||||
|
|
Loading…
Reference in New Issue