From c07e30a89c691e11e2bace073b0c587e54d15d9d Mon Sep 17 00:00:00 2001 From: jorgep Date: Wed, 8 Jan 2025 12:48:28 +0100 Subject: [PATCH 1/6] fix: prevent deleting absences for past dates --- db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql | 2 ++ modules/worker/back/methods/worker/deleteAbsence.js | 6 ++++++ 2 files changed, 8 insertions(+) create mode 100644 db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql diff --git a/db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql b/db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql new file mode 100644 index 000000000..8ab24cb0d --- /dev/null +++ b/db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql @@ -0,0 +1,2 @@ +INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) + VALUES ('Worker','canDeleteAbsenceInPast','WRITE','ALLOW','ROLE','hr'); \ No newline at end of file diff --git a/modules/worker/back/methods/worker/deleteAbsence.js b/modules/worker/back/methods/worker/deleteAbsence.js index b71d077a4..a7c6efc21 100644 --- a/modules/worker/back/methods/worker/deleteAbsence.js +++ b/modules/worker/back/methods/worker/deleteAbsence.js @@ -53,6 +53,12 @@ module.exports = Self => { } } }, myOptions); + const canDeleteAbsenceInPast = + await models.ACL.checkAccessAcl(ctx, 'Worker', 'canDeleteAbsenceInPast', 'WRITE'); + + if (!canDeleteAbsenceInPast && Date.vnNow() > absence.dated.getTime()) + throw new UserError(`Holidays to past days not available`); + const result = await absence.destroy(myOptions); const labour = absence.labour(); const department = labour && labour.department(); From f557b41feb782d1f1eb9788ec3d8fd413f2d3284 Mon Sep 17 00:00:00 2001 From: jorgep Date: Wed, 8 Jan 2025 13:52:50 +0100 Subject: [PATCH 2/6] fix: tests --- .../worker/specs/deleteAbsence.spec.js | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js index 0f3f913dc..c0d05e4a2 100644 --- a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js @@ -4,6 +4,8 @@ const LoopBackContext = require('loopback-context'); describe('Worker deleteAbsence()', () => { const businessId = 18; const workerId = 18; + const hrId = 37; + const salesBossId = 19; const activeCtx = { accessToken: {userId: 1106}, headers: {origin: 'http://localhost'} @@ -50,16 +52,16 @@ describe('Worker deleteAbsence()', () => { }); it('should successfully delete an absence', async() => { - activeCtx.accessToken.userId = 19; + activeCtx.accessToken.userId = salesBossId; const tx = await app.models.Calendar.beginTransaction({}); - + const pastDate = new Date(Date.vnNow() + 24 * 60 * 60 * 1000); try { const options = {transaction: tx}; const createdAbsence = await app.models.Calendar.create({ businessFk: businessId, dayOffTypeFk: 1, - dated: Date.vnNew() + dated: pastDate }, options); ctx.args = {absenceId: createdAbsence.id}; @@ -76,4 +78,61 @@ describe('Worker deleteAbsence()', () => { throw e; } }); + + it('should successfully delete an absence if the user is HR even if the date is in the past', async() => { + activeCtx.accessToken.userId = hrId; + const tx = await app.models.Calendar.beginTransaction({}); + + try { + const options = {transaction: tx}; + const pastDate = new Date(Date.vnNow() - 24 * 60 * 60 * 1000); // Restar un día + const createdAbsence = await app.models.Calendar.create({ + businessFk: businessId, + dayOffTypeFk: 1, + dated: pastDate + }, options); + + ctx.args = {absenceId: createdAbsence.id}; + await app.models.Worker.deleteAbsence(ctx, workerId, options); + + const deletedAbsence = await app.models.Calendar.findById(createdAbsence.id, null, options); + + expect(deletedAbsence).toBeNull(); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should throw an error if the date is in the past', async() => { + activeCtx.accessToken.userId = salesBossId; + const tx = await app.models.Calendar.beginTransaction({}); + + let error; + try { + const options = {transaction: tx}; + const pastDate = new Date(Date.vnNow() - 24 * 60 * 60 * 1000); + const createdAbsence = await app.models.Calendar.create({ + businessFk: businessId, + dayOffTypeFk: 1, + dated: pastDate + }, options); + + ctx.args = {absenceId: createdAbsence.id}; + await app.models.Worker.deleteAbsence(ctx, workerId, options); + + const deletedAbsence = await app.models.Calendar.findById(createdAbsence.id, null, options); + + expect(deletedAbsence).toBeNull(); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe('Holidays to past days not available'); + }); }); From 838617e3f667fdb9ad14d454cdaa1854eca7d6d5 Mon Sep 17 00:00:00 2001 From: jorgep Date: Wed, 8 Jan 2025 15:47:53 +0100 Subject: [PATCH 3/6] fix: update access control for modifying absences in the past --- db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql | 4 +++- modules/worker/back/methods/worker/createAbsence.js | 6 +++--- modules/worker/back/methods/worker/deleteAbsence.js | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql b/db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql index 8ab24cb0d..f3e0355a8 100644 --- a/db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql +++ b/db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql @@ -1,2 +1,4 @@ +DELETE FROM salix.ACL WHERE property = 'canCreateAbsenceInPast'; + INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) - VALUES ('Worker','canDeleteAbsenceInPast','WRITE','ALLOW','ROLE','hr'); \ No newline at end of file + VALUES ('Worker','canModifyAbsenceInPast','WRITE','ALLOW','ROLE','hr'); diff --git a/modules/worker/back/methods/worker/createAbsence.js b/modules/worker/back/methods/worker/createAbsence.js index 93ca7fd89..36781bc3f 100644 --- a/modules/worker/back/methods/worker/createAbsence.js +++ b/modules/worker/back/methods/worker/createAbsence.js @@ -58,12 +58,12 @@ module.exports = Self => { if (!isSubordinate || (isSubordinate && userId == id && !isTeamBoss)) throw new UserError(`You don't have enough privileges`); - const canCreateAbsenceInPast = - await models.ACL.checkAccessAcl(ctx, 'Worker', 'canCreateAbsenceInPast', 'WRITE'); + const canModifyAbsenceInPast = + await models.ACL.checkAccessAcl(ctx, 'Worker', 'canModifyAbsenceInPast', 'WRITE'); const now = Date.vnNew(); const newDate = new Date(args.dated).getTime(); - if ((now.getTime() > newDate) && !canCreateAbsenceInPast) + if ((now.getTime() > newDate) && !canModifyAbsenceInPast) throw new UserError(`Holidays to past days not available`); const labour = await models.WorkerLabour.findById(args.businessFk, diff --git a/modules/worker/back/methods/worker/deleteAbsence.js b/modules/worker/back/methods/worker/deleteAbsence.js index a7c6efc21..11a8cb0c1 100644 --- a/modules/worker/back/methods/worker/deleteAbsence.js +++ b/modules/worker/back/methods/worker/deleteAbsence.js @@ -53,10 +53,10 @@ module.exports = Self => { } } }, myOptions); - const canDeleteAbsenceInPast = - await models.ACL.checkAccessAcl(ctx, 'Worker', 'canDeleteAbsenceInPast', 'WRITE'); + const canModifyAbsenceInPast = + await models.ACL.checkAccessAcl(ctx, 'Worker', 'canModifyAbsenceInPast', 'WRITE'); - if (!canDeleteAbsenceInPast && Date.vnNow() > absence.dated.getTime()) + if (!canModifyAbsenceInPast && Date.vnNow() > absence.dated.getTime()) throw new UserError(`Holidays to past days not available`); const result = await absence.destroy(myOptions); From 412638d59000f04fbe10aa45c4429230e57f1a41 Mon Sep 17 00:00:00 2001 From: jorgep Date: Wed, 8 Jan 2025 18:27:23 +0100 Subject: [PATCH 4/6] fix: refactor access control for modifying past absences --- modules/worker/back/methods/worker/createAbsence.js | 4 +--- modules/worker/back/methods/worker/deleteAbsence.js | 4 +--- .../worker/back/methods/worker/specs/createAbsence.spec.js | 2 +- .../worker/back/methods/worker/specs/deleteAbsence.spec.js | 2 +- modules/worker/back/models/worker.js | 7 +++++++ 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/modules/worker/back/methods/worker/createAbsence.js b/modules/worker/back/methods/worker/createAbsence.js index 36781bc3f..dc716c95d 100644 --- a/modules/worker/back/methods/worker/createAbsence.js +++ b/modules/worker/back/methods/worker/createAbsence.js @@ -58,12 +58,10 @@ module.exports = Self => { if (!isSubordinate || (isSubordinate && userId == id && !isTeamBoss)) throw new UserError(`You don't have enough privileges`); - const canModifyAbsenceInPast = - await models.ACL.checkAccessAcl(ctx, 'Worker', 'canModifyAbsenceInPast', 'WRITE'); const now = Date.vnNew(); const newDate = new Date(args.dated).getTime(); - if ((now.getTime() > newDate) && !canModifyAbsenceInPast) + if (!await Self.canModifyAbsenceInPast(ctx, newDate)) throw new UserError(`Holidays to past days not available`); const labour = await models.WorkerLabour.findById(args.businessFk, diff --git a/modules/worker/back/methods/worker/deleteAbsence.js b/modules/worker/back/methods/worker/deleteAbsence.js index 11a8cb0c1..596f8f28d 100644 --- a/modules/worker/back/methods/worker/deleteAbsence.js +++ b/modules/worker/back/methods/worker/deleteAbsence.js @@ -53,10 +53,8 @@ module.exports = Self => { } } }, myOptions); - const canModifyAbsenceInPast = - await models.ACL.checkAccessAcl(ctx, 'Worker', 'canModifyAbsenceInPast', 'WRITE'); - if (!canModifyAbsenceInPast && Date.vnNow() > absence.dated.getTime()) + if (!await Self.canModifyAbsenceInPast(ctx, absence.dated.getTime())) throw new UserError(`Holidays to past days not available`); const result = await absence.destroy(myOptions); diff --git a/modules/worker/back/methods/worker/specs/createAbsence.spec.js b/modules/worker/back/methods/worker/specs/createAbsence.spec.js index 1c7efcd28..b6600048f 100644 --- a/modules/worker/back/methods/worker/specs/createAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/createAbsence.spec.js @@ -1,7 +1,7 @@ const app = require('vn-loopback/server/server'); const LoopBackContext = require('loopback-context'); -describe('Worker createAbsence()', () => { +fdescribe('Worker createAbsence()', () => { const workerId = 18; it('should return an error for a user without enough privileges', async() => { diff --git a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js index c0d05e4a2..dfbcd9835 100644 --- a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js @@ -1,7 +1,7 @@ const app = require('vn-loopback/server/server'); const LoopBackContext = require('loopback-context'); -describe('Worker deleteAbsence()', () => { +fdescribe('Worker deleteAbsence()', () => { const businessId = 18; const workerId = 18; const hrId = 37; diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js index 3351c348c..e1adca77e 100644 --- a/modules/worker/back/models/worker.js +++ b/modules/worker/back/models/worker.js @@ -26,6 +26,13 @@ module.exports = Self => { message: 'Invalid TIN' }); + Self.canModifyAbsenceInPast = async(ctx, time) => { + const hasPrivs = await Self.app.models.ACL.checkAccessAcl(ctx, 'Worker', 'canModifyAbsenceInPast', 'WRITE'); + const now = Date.vnNew(); + now.setHours(0, 0, 0, 0); + return hasPrivs || now.getTime() < time; + }; + async function tinIsValid(err, done) { const country = await Self.app.models.Country.findOne({ fields: ['code'], From b12c3bb72f8d182983bfd89d64c55237bf20f834 Mon Sep 17 00:00:00 2001 From: jorgep Date: Wed, 8 Jan 2025 18:28:00 +0100 Subject: [PATCH 5/6] fix: enable tests for createAbsence and deleteAbsence methods --- modules/worker/back/methods/worker/specs/createAbsence.spec.js | 2 +- modules/worker/back/methods/worker/specs/deleteAbsence.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/worker/back/methods/worker/specs/createAbsence.spec.js b/modules/worker/back/methods/worker/specs/createAbsence.spec.js index b6600048f..1c7efcd28 100644 --- a/modules/worker/back/methods/worker/specs/createAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/createAbsence.spec.js @@ -1,7 +1,7 @@ const app = require('vn-loopback/server/server'); const LoopBackContext = require('loopback-context'); -fdescribe('Worker createAbsence()', () => { +describe('Worker createAbsence()', () => { const workerId = 18; it('should return an error for a user without enough privileges', async() => { diff --git a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js index dfbcd9835..c0d05e4a2 100644 --- a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js @@ -1,7 +1,7 @@ const app = require('vn-loopback/server/server'); const LoopBackContext = require('loopback-context'); -fdescribe('Worker deleteAbsence()', () => { +describe('Worker deleteAbsence()', () => { const businessId = 18; const workerId = 18; const hrId = 37; From 50a95ed3c4bdb1396d1d1c1ad12b81ed301e1561 Mon Sep 17 00:00:00 2001 From: jorgep Date: Wed, 8 Jan 2025 18:30:33 +0100 Subject: [PATCH 6/6] fix: correct variable name in canModifyAbsenceInPast method --- modules/worker/back/models/worker.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js index e1adca77e..2e45b78da 100644 --- a/modules/worker/back/models/worker.js +++ b/modules/worker/back/models/worker.js @@ -28,9 +28,9 @@ module.exports = Self => { Self.canModifyAbsenceInPast = async(ctx, time) => { const hasPrivs = await Self.app.models.ACL.checkAccessAcl(ctx, 'Worker', 'canModifyAbsenceInPast', 'WRITE'); - const now = Date.vnNew(); - now.setHours(0, 0, 0, 0); - return hasPrivs || now.getTime() < time; + const today = Date.vnNew(); + today.setHours(0, 0, 0, 0); + return hasPrivs || today.getTime() < time; }; async function tinIsValid(err, done) {