1.2
This commit is contained in:
parent
1fd0a1b094
commit
e20241ff74
@ -3,6 +3,7 @@ const db = require('../models');
|
|||||||
const FileDBApi = require('./file');
|
const FileDBApi = require('./file');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const Utils = require('../utils');
|
const Utils = require('../utils');
|
||||||
|
const { isRestrictedPayrollUser } = require('../../security/payrollAccess');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -205,12 +206,17 @@ module.exports = class Employee_pay_typesDBApi {
|
|||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
|
|
||||||
const employee_pay_types = await db.employee_pay_types.findOne(
|
const employee_pay_types = await db.employee_pay_types.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (employee_pay_types && isRestrictedPayrollUser(currentUser) && employee_pay_types.employeeId !== currentUser.id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!employee_pay_types) {
|
if (!employee_pay_types) {
|
||||||
return employee_pay_types;
|
return employee_pay_types;
|
||||||
}
|
}
|
||||||
@ -249,6 +255,8 @@ module.exports = class Employee_pay_typesDBApi {
|
|||||||
filter,
|
filter,
|
||||||
options
|
options
|
||||||
) {
|
) {
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
|
const effectiveEmployeeFilter = isRestrictedPayrollUser(currentUser) ? currentUser.id : filter.employee;
|
||||||
const limit = filter.limit || 0;
|
const limit = filter.limit || 0;
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let where = {};
|
let where = {};
|
||||||
@ -270,12 +278,12 @@ module.exports = class Employee_pay_typesDBApi {
|
|||||||
model: db.users,
|
model: db.users,
|
||||||
as: 'employee',
|
as: 'employee',
|
||||||
|
|
||||||
where: filter.employee ? {
|
where: effectiveEmployeeFilter ? {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ id: { [Op.in]: filter.employee.split('|').map(term => Utils.uuid(term)) } },
|
{ id: { [Op.in]: effectiveEmployeeFilter.split('|').map(term => Utils.uuid(term)) } },
|
||||||
{
|
{
|
||||||
firstName: {
|
firstName: {
|
||||||
[Op.or]: filter.employee.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
[Op.or]: effectiveEmployeeFilter.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -447,7 +455,8 @@ module.exports = class Employee_pay_typesDBApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findAllAutocomplete(query, limit, offset, ) {
|
static async findAllAutocomplete(query, limit, offset, options) {
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
let where = {};
|
let where = {};
|
||||||
|
|
||||||
|
|
||||||
@ -465,6 +474,13 @@ module.exports = class Employee_pay_typesDBApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser)) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
employeeId: currentUser.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const records = await db.employee_pay_types.findAll({
|
const records = await db.employee_pay_types.findAll({
|
||||||
attributes: [ 'id', 'active' ],
|
attributes: [ 'id', 'active' ],
|
||||||
where,
|
where,
|
||||||
|
|||||||
@ -3,6 +3,7 @@ const db = require('../models');
|
|||||||
const FileDBApi = require('./file');
|
const FileDBApi = require('./file');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const Utils = require('../utils');
|
const Utils = require('../utils');
|
||||||
|
const { isRestrictedPayrollUser } = require('../../security/payrollAccess');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -320,12 +321,17 @@ module.exports = class Job_logsDBApi {
|
|||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
|
|
||||||
const job_logs = await db.job_logs.findOne(
|
const job_logs = await db.job_logs.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (job_logs && isRestrictedPayrollUser(currentUser) && job_logs.employeeId !== currentUser.id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!job_logs) {
|
if (!job_logs) {
|
||||||
return job_logs;
|
return job_logs;
|
||||||
}
|
}
|
||||||
@ -382,6 +388,7 @@ module.exports = class Job_logsDBApi {
|
|||||||
filter,
|
filter,
|
||||||
options
|
options
|
||||||
) {
|
) {
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
const limit = filter.limit || 0;
|
const limit = filter.limit || 0;
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let where = {};
|
let where = {};
|
||||||
@ -486,6 +493,13 @@ module.exports = class Job_logsDBApi {
|
|||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser)) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
employeeId: currentUser.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
if (filter.id) {
|
if (filter.id) {
|
||||||
where = {
|
where = {
|
||||||
@ -752,7 +766,8 @@ module.exports = class Job_logsDBApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findAllAutocomplete(query, limit, offset, ) {
|
static async findAllAutocomplete(query, limit, offset, options) {
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
let where = {};
|
let where = {};
|
||||||
|
|
||||||
|
|
||||||
@ -770,6 +785,13 @@ module.exports = class Job_logsDBApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser)) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
employeeId: currentUser.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const records = await db.job_logs.findAll({
|
const records = await db.job_logs.findAll({
|
||||||
attributes: [ 'id', 'job_address' ],
|
attributes: [ 'id', 'job_address' ],
|
||||||
where,
|
where,
|
||||||
|
|||||||
@ -3,6 +3,7 @@ const db = require('../models');
|
|||||||
const FileDBApi = require('./file');
|
const FileDBApi = require('./file');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const Utils = require('../utils');
|
const Utils = require('../utils');
|
||||||
|
const { isRestrictedPayrollUser } = require('../../security/payrollAccess');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -218,9 +219,20 @@ module.exports = class Pay_typesDBApi {
|
|||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
|
const accessInclude = isRestrictedPayrollUser(currentUser) ? [{
|
||||||
|
model: db.employee_pay_types,
|
||||||
|
as: 'employee_pay_types_pay_type',
|
||||||
|
required: true,
|
||||||
|
attributes: [],
|
||||||
|
where: {
|
||||||
|
employeeId: currentUser.id,
|
||||||
|
active: true,
|
||||||
|
},
|
||||||
|
}] : undefined;
|
||||||
|
|
||||||
const pay_types = await db.pay_types.findOne(
|
const pay_types = await db.pay_types.findOne(
|
||||||
{ where },
|
{ where, include: accessInclude },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -238,13 +250,15 @@ module.exports = class Pay_typesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
output.employee_pay_types_pay_type = await pay_types.getEmployee_pay_types_pay_type({
|
output.employee_pay_types_pay_type = await pay_types.getEmployee_pay_types_pay_type({
|
||||||
transaction
|
transaction,
|
||||||
|
where: isRestrictedPayrollUser(currentUser) ? { employeeId: currentUser.id, active: true } : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.job_logs_pay_type = await pay_types.getJob_logs_pay_type({
|
output.job_logs_pay_type = await pay_types.getJob_logs_pay_type({
|
||||||
transaction
|
transaction,
|
||||||
|
where: isRestrictedPayrollUser(currentUser) ? { employeeId: currentUser.id } : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -260,6 +274,7 @@ module.exports = class Pay_typesDBApi {
|
|||||||
filter,
|
filter,
|
||||||
options
|
options
|
||||||
) {
|
) {
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
const limit = filter.limit || 0;
|
const limit = filter.limit || 0;
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let where = {};
|
let where = {};
|
||||||
@ -281,6 +296,22 @@ module.exports = class Pay_typesDBApi {
|
|||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser)) {
|
||||||
|
include = [
|
||||||
|
{
|
||||||
|
model: db.employee_pay_types,
|
||||||
|
as: 'employee_pay_types_pay_type',
|
||||||
|
required: true,
|
||||||
|
attributes: [],
|
||||||
|
where: {
|
||||||
|
employeeId: currentUser.id,
|
||||||
|
active: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...include,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
if (filter.id) {
|
if (filter.id) {
|
||||||
where = {
|
where = {
|
||||||
@ -449,7 +480,8 @@ module.exports = class Pay_typesDBApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findAllAutocomplete(query, limit, offset, ) {
|
static async findAllAutocomplete(query, limit, offset, options) {
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
let where = {};
|
let where = {};
|
||||||
|
|
||||||
|
|
||||||
@ -470,6 +502,16 @@ module.exports = class Pay_typesDBApi {
|
|||||||
const records = await db.pay_types.findAll({
|
const records = await db.pay_types.findAll({
|
||||||
attributes: [ 'id', 'name' ],
|
attributes: [ 'id', 'name' ],
|
||||||
where,
|
where,
|
||||||
|
include: isRestrictedPayrollUser(currentUser) ? [{
|
||||||
|
model: db.employee_pay_types,
|
||||||
|
as: 'employee_pay_types_pay_type',
|
||||||
|
required: true,
|
||||||
|
attributes: [],
|
||||||
|
where: {
|
||||||
|
employeeId: currentUser.id,
|
||||||
|
active: true,
|
||||||
|
},
|
||||||
|
}] : undefined,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['name', 'ASC']],
|
orderBy: [['name', 'ASC']],
|
||||||
|
|||||||
@ -3,6 +3,7 @@ const db = require('../models');
|
|||||||
const FileDBApi = require('./file');
|
const FileDBApi = require('./file');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const Utils = require('../utils');
|
const Utils = require('../utils');
|
||||||
|
const { isRestrictedPayrollUser } = require('../../security/payrollAccess');
|
||||||
|
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
const config = require('../../config');
|
const config = require('../../config');
|
||||||
@ -387,9 +388,19 @@ module.exports = class UsersDBApi {
|
|||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
|
const effectiveWhere = { ...(where || {}) };
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser)) {
|
||||||
|
if (effectiveWhere.id && Utils.uuid(effectiveWhere.id) !== currentUser.id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
effectiveWhere.id = currentUser.id;
|
||||||
|
}
|
||||||
|
|
||||||
const users = await db.users.findOne(
|
const users = await db.users.findOne(
|
||||||
{ where },
|
{ where: effectiveWhere },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -408,6 +419,7 @@ module.exports = class UsersDBApi {
|
|||||||
|
|
||||||
output.employee_pay_types_employee = await users.getEmployee_pay_types_employee({
|
output.employee_pay_types_employee = await users.getEmployee_pay_types_employee({
|
||||||
transaction,
|
transaction,
|
||||||
|
where: isRestrictedPayrollUser(currentUser) ? { employeeId: currentUser.id, active: true } : undefined,
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: db.pay_types,
|
model: db.pay_types,
|
||||||
@ -460,6 +472,7 @@ module.exports = class UsersDBApi {
|
|||||||
filter,
|
filter,
|
||||||
options
|
options
|
||||||
) {
|
) {
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
const limit = filter.limit || 0;
|
const limit = filter.limit || 0;
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let where = {};
|
let where = {};
|
||||||
@ -524,6 +537,13 @@ module.exports = class UsersDBApi {
|
|||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser)) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
id: currentUser.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
if (filter.id) {
|
if (filter.id) {
|
||||||
where = {
|
where = {
|
||||||
@ -783,7 +803,8 @@ module.exports = class UsersDBApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findAllAutocomplete(query, limit, offset, ) {
|
static async findAllAutocomplete(query, limit, offset, options) {
|
||||||
|
const currentUser = options?.currentUser;
|
||||||
let where = {};
|
let where = {};
|
||||||
|
|
||||||
|
|
||||||
@ -801,6 +822,13 @@ module.exports = class UsersDBApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser)) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
id: currentUser.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const records = await db.users.findAll({
|
const records = await db.users.findAll({
|
||||||
attributes: [ 'id', 'firstName' ],
|
attributes: [ 'id', 'firstName' ],
|
||||||
where,
|
where,
|
||||||
|
|||||||
@ -335,7 +335,6 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await Employee_pay_typesDBApi.findAll(
|
const payload = await Employee_pay_typesDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -373,7 +372,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
|
{ currentUser: req.currentUser },
|
||||||
);
|
);
|
||||||
|
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
@ -414,6 +413,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await Employee_pay_typesDBApi.findBy(
|
const payload = await Employee_pay_typesDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
|
{ currentUser: req.currentUser },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -355,7 +355,6 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await Job_logsDBApi.findAll(
|
const payload = await Job_logsDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -393,7 +392,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
|
{ currentUser: req.currentUser },
|
||||||
);
|
);
|
||||||
|
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
@ -434,6 +433,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await Job_logsDBApi.findBy(
|
const payload = await Job_logsDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
|
{ currentUser: req.currentUser },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -348,7 +348,6 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await Pay_typesDBApi.findAll(
|
const payload = await Pay_typesDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -386,7 +385,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
|
{ currentUser: req.currentUser },
|
||||||
);
|
);
|
||||||
|
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
@ -427,6 +426,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await Pay_typesDBApi.findBy(
|
const payload = await Pay_typesDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
|
{ currentUser: req.currentUser },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -347,7 +347,6 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await UsersDBApi.findAll(
|
const payload = await UsersDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -385,7 +384,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
|
{ currentUser: req.currentUser },
|
||||||
);
|
);
|
||||||
|
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
@ -426,11 +425,12 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await UsersDBApi.findBy(
|
const payload = await UsersDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
|
{ currentUser: req.currentUser },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (payload) {
|
||||||
delete payload.password;
|
delete payload.password;
|
||||||
|
}
|
||||||
|
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|||||||
19
backend/src/security/payrollAccess.js
Normal file
19
backend/src/security/payrollAccess.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const PRIVILEGED_PAYROLL_ROLE_NAMES = new Set([
|
||||||
|
'Administrator',
|
||||||
|
'System Owner',
|
||||||
|
'Payroll Manager',
|
||||||
|
'Operations Manager',
|
||||||
|
]);
|
||||||
|
|
||||||
|
function isRestrictedPayrollUser(currentUser) {
|
||||||
|
if (!currentUser?.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !PRIVILEGED_PAYROLL_ROLE_NAMES.has(currentUser?.app_role?.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
PRIVILEGED_PAYROLL_ROLE_NAMES,
|
||||||
|
isRestrictedPayrollUser,
|
||||||
|
};
|
||||||
@ -7,8 +7,28 @@ const csv = require('csv-parser');
|
|||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const config = require('../config');
|
const config = require('../config');
|
||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
|
const { isRestrictedPayrollUser } = require('../security/payrollAccess');
|
||||||
|
|
||||||
module.exports = class Job_logsService {
|
module.exports = class Job_logsService {
|
||||||
|
static async ensureRestrictedPayTypeAccess(payTypeId, currentUser, transaction) {
|
||||||
|
if (!isRestrictedPayrollUser(currentUser) || !payTypeId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const assignment = await db.employee_pay_types.findOne({
|
||||||
|
where: {
|
||||||
|
employeeId: currentUser.id,
|
||||||
|
pay_typeId: payTypeId,
|
||||||
|
active: true,
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!assignment) {
|
||||||
|
throw new ValidationError('errors.forbidden.message');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static async create(data, currentUser) {
|
static async create(data, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
@ -23,8 +43,16 @@ module.exports = class Job_logsService {
|
|||||||
customerId = customer.id;
|
customerId = customer.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const jobPayload = { ...data, customer: customerId };
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser)) {
|
||||||
|
jobPayload.employee = currentUser.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Job_logsService.ensureRestrictedPayTypeAccess(jobPayload.pay_type, currentUser, transaction);
|
||||||
|
|
||||||
const createdJob = await Job_logsDBApi.create(
|
const createdJob = await Job_logsDBApi.create(
|
||||||
{ ...data, customer: customerId },
|
jobPayload,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
transaction,
|
transaction,
|
||||||
@ -93,7 +121,10 @@ module.exports = class Job_logsService {
|
|||||||
try {
|
try {
|
||||||
let job_logs = await Job_logsDBApi.findBy(
|
let job_logs = await Job_logsDBApi.findBy(
|
||||||
{id},
|
{id},
|
||||||
{transaction},
|
{
|
||||||
|
transaction,
|
||||||
|
currentUser,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!job_logs) {
|
if (!job_logs) {
|
||||||
@ -101,6 +132,10 @@ module.exports = class Job_logsService {
|
|||||||
'job_logsNotFound',
|
'job_logsNotFound',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser) && job_logs.employeeId !== currentUser.id) {
|
||||||
|
throw new ValidationError('errors.forbidden.message');
|
||||||
|
}
|
||||||
|
|
||||||
let customerId = data.customer;
|
let customerId = data.customer;
|
||||||
|
|
||||||
@ -113,9 +148,21 @@ module.exports = class Job_logsService {
|
|||||||
customerId = customer.id;
|
customerId = customer.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const jobPayload = { ...data, customer: customerId };
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser)) {
|
||||||
|
jobPayload.employee = currentUser.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Job_logsService.ensureRestrictedPayTypeAccess(
|
||||||
|
jobPayload.pay_type !== undefined ? jobPayload.pay_type : job_logs.pay_typeId,
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
);
|
||||||
|
|
||||||
const updatedJob_logs = await Job_logsDBApi.update(
|
const updatedJob_logs = await Job_logsDBApi.update(
|
||||||
id,
|
id,
|
||||||
{ ...data, customer: customerId },
|
jobPayload,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
transaction,
|
transaction,
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { MenuAsideItem } from '../interfaces'
|
|||||||
import AsideMenuItem from './AsideMenuItem'
|
import AsideMenuItem from './AsideMenuItem'
|
||||||
import {useAppSelector} from "../stores/hooks";
|
import {useAppSelector} from "../stores/hooks";
|
||||||
import {hasPermission} from "../helpers/userPermissions";
|
import {hasPermission} from "../helpers/userPermissions";
|
||||||
|
import { isBlockedWorkerRoute, isRestrictedPayrollUser } from '../helpers/accessControl';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
menu: MenuAsideItem[]
|
menu: MenuAsideItem[]
|
||||||
@ -18,9 +19,10 @@ export default function AsideMenuList({ menu, isDropdownList = false, className
|
|||||||
return (
|
return (
|
||||||
<ul className={className}>
|
<ul className={className}>
|
||||||
{menu.map((item, index) => {
|
{menu.map((item, index) => {
|
||||||
|
if (!hasPermission(currentUser, item.permissions)) return null;
|
||||||
if (!hasPermission(currentUser, item.permissions)) return null;
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser) && isBlockedWorkerRoute(item.href)) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<AsideMenuItem
|
<AsideMenuItem
|
||||||
|
|||||||
24
frontend/src/helpers/accessControl.ts
Normal file
24
frontend/src/helpers/accessControl.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const PRIVILEGED_PAYROLL_ROLE_NAMES = new Set([
|
||||||
|
'Administrator',
|
||||||
|
'System Owner',
|
||||||
|
'Payroll Manager',
|
||||||
|
'Operations Manager',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const BLOCKED_WORKER_ROUTE_PREFIXES = ['/users', '/pay_types', '/employee_pay_types'];
|
||||||
|
|
||||||
|
export function isRestrictedPayrollUser(user: any) {
|
||||||
|
if (!user?.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !PRIVILEGED_PAYROLL_ROLE_NAMES.has(user?.app_role?.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBlockedWorkerRoute(pathname?: string) {
|
||||||
|
if (!pathname) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BLOCKED_WORKER_ROUTE_PREFIXES.some((prefix) => pathname.startsWith(prefix));
|
||||||
|
}
|
||||||
@ -14,6 +14,7 @@ import { useRouter } from 'next/router'
|
|||||||
import {findMe, logoutUser} from "../stores/authSlice";
|
import {findMe, logoutUser} from "../stores/authSlice";
|
||||||
|
|
||||||
import {hasPermission} from "../helpers/userPermissions";
|
import {hasPermission} from "../helpers/userPermissions";
|
||||||
|
import { isBlockedWorkerRoute, isRestrictedPayrollUser } from '../helpers/accessControl';
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -61,7 +62,15 @@ export default function LayoutAuthenticated({
|
|||||||
if (!permission || !currentUser) return;
|
if (!permission || !currentUser) return;
|
||||||
|
|
||||||
if (!hasPermission(currentUser, permission)) router.push('/error');
|
if (!hasPermission(currentUser, permission)) router.push('/error');
|
||||||
}, [currentUser, permission]);
|
}, [currentUser, permission, router]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!currentUser) return;
|
||||||
|
|
||||||
|
if (isRestrictedPayrollUser(currentUser) && isBlockedWorkerRoute(router.pathname)) {
|
||||||
|
router.replace('/my-logs');
|
||||||
|
}
|
||||||
|
}, [currentUser, router, router.pathname]);
|
||||||
|
|
||||||
|
|
||||||
const darkMode = useAppSelector((state) => state.style.darkMode)
|
const darkMode = useAppSelector((state) => state.style.darkMode)
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { getPageTitle } from '../config'
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
import { hasPermission } from "../helpers/userPermissions";
|
import { hasPermission } from "../helpers/userPermissions";
|
||||||
|
import { isRestrictedPayrollUser } from '../helpers/accessControl';
|
||||||
import { fetchWidgets } from '../stores/roles/rolesSlice';
|
import { fetchWidgets } from '../stores/roles/rolesSlice';
|
||||||
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
|
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
|
||||||
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
|
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
|
||||||
@ -157,7 +158,7 @@ const Dashboard = () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_USERS') && <Link href={'/users/users-list'}>
|
{!isRestrictedPayrollUser(currentUser) && hasPermission(currentUser, 'READ_USERS') && <Link href={'/users/users-list'}>
|
||||||
<div
|
<div
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||||
>
|
>
|
||||||
@ -297,7 +298,7 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Link>}
|
</Link>}
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_PAY_TYPES') && <Link href={'/pay_types/pay_types-list'}>
|
{!isRestrictedPayrollUser(currentUser) && hasPermission(currentUser, 'READ_PAY_TYPES') && <Link href={'/pay_types/pay_types-list'}>
|
||||||
<div
|
<div
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||||
>
|
>
|
||||||
@ -325,7 +326,7 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Link>}
|
</Link>}
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_EMPLOYEE_PAY_TYPES') && <Link href={'/employee_pay_types/employee_pay_types-list'}>
|
{!isRestrictedPayrollUser(currentUser) && hasPermission(currentUser, 'READ_EMPLOYEE_PAY_TYPES') && <Link href={'/employee_pay_types/employee_pay_types-list'}>
|
||||||
<div
|
<div
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||||
>
|
>
|
||||||
|
|||||||
1
frontend/tsconfig.tsbuildinfo
Normal file
1
frontend/tsconfig.tsbuildinfo
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user