This commit is contained in:
parent
b37bde2ba7
commit
e7d16dc8ba
|
@ -0,0 +1,80 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('getList', {
|
||||||
|
description: 'Get list of the available and active notification subscriptions',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
description: 'User to modify',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/getList`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getList = async(id, options) => {
|
||||||
|
const notifications = [];
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const activeNotifications = await models.NotificationSubscription.find({
|
||||||
|
include: {relation: 'notification'},
|
||||||
|
where: {userFk: id}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const roles = await models.RoleMapping.find({
|
||||||
|
fields: ['roleId'],
|
||||||
|
where: {principalId: id}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const availableNotifications = await models.NotificationAcl.find({
|
||||||
|
include: {relation: 'notification'},
|
||||||
|
where: {
|
||||||
|
roleFk: {
|
||||||
|
inq: roles.map(role => {
|
||||||
|
return role.roleId;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
activeNotifications.forEach(subscription => {
|
||||||
|
notifications.push({
|
||||||
|
id: subscription.id,
|
||||||
|
notificationFk: subscription.notificationFk,
|
||||||
|
name: subscription.notification().name,
|
||||||
|
description: subscription.notification().description,
|
||||||
|
active: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
availableNotifications.forEach(acl => {
|
||||||
|
const activeNotif = notifications.find(
|
||||||
|
notif => notif.notificationFk === acl.notificationFk
|
||||||
|
);
|
||||||
|
if (!activeNotif) {
|
||||||
|
notifications.push({
|
||||||
|
id: null,
|
||||||
|
notificationFk: acl.notificationFk,
|
||||||
|
name: acl.notification().name,
|
||||||
|
description: acl.notification().description,
|
||||||
|
active: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return notifications;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,38 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('NotificationSubscription getList()', () => {
|
||||||
|
it('should return a list of available and active notifications of a user', async() => {
|
||||||
|
const userId = 1109;
|
||||||
|
const activeNotifications = await models.NotificationSubscription.find({
|
||||||
|
where: {userFk: userId}
|
||||||
|
});
|
||||||
|
const roles = await models.RoleMapping.find({
|
||||||
|
fields: ['roleId'],
|
||||||
|
where: {principalId: userId}
|
||||||
|
});
|
||||||
|
const availableNotifications = await models.NotificationAcl.find({
|
||||||
|
where: {
|
||||||
|
roleFk: {
|
||||||
|
inq: roles.map(role => {
|
||||||
|
return role.roleId;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await models.NotificationSubscription.getList(userId);
|
||||||
|
|
||||||
|
expect(result.filter(notification => notification.active == true).length)
|
||||||
|
.toEqual(activeNotifications.length);
|
||||||
|
|
||||||
|
expect(result.filter(notification => notification.active == false).length)
|
||||||
|
.toEqual(availableNotifications.length - activeNotifications.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty from a non existent user', async() => {
|
||||||
|
const userId = 123456789;
|
||||||
|
const result = await models.NotificationSubscription.getList(userId);
|
||||||
|
|
||||||
|
expect(result.length).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,62 +1,40 @@
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
|
require('../methods/notification/getList')(Self);
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
Self.observe('before save', async function(ctx) {
|
||||||
|
await checkModifyPermission(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.observe('before delete', async function(ctx) {
|
||||||
|
await checkModifyPermission(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function checkModifyPermission(ctx) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
const instance = ctx.instance;
|
||||||
const userId = ctx.options.accessToken.userId;
|
const userId = ctx.options.accessToken.userId;
|
||||||
const user = await ctx.instance.userFk;
|
|
||||||
const modifiedUser = await getUserToModify(null, user, models);
|
|
||||||
|
|
||||||
if (userId != modifiedUser.id && userId != modifiedUser.bossFk)
|
let notificationFk;
|
||||||
throw new UserError('You dont have permission to modify this user');
|
let workerId;
|
||||||
});
|
|
||||||
|
|
||||||
Self.remoteMethod('deleteNotification', {
|
if (instance) {
|
||||||
description: 'Deletes a notification subscription',
|
notificationFk = instance.notificationFk;
|
||||||
accepts: [
|
workerId = instance.userFk;
|
||||||
{
|
} else {
|
||||||
arg: 'ctx',
|
const notificationSubscription = await models.NotificationSubscription.findById(ctx.where.id);
|
||||||
type: 'object',
|
notificationFk = notificationSubscription.notificationFk;
|
||||||
http: {source: 'context'}
|
workerId = notificationSubscription.userFk;
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'notificationId',
|
|
||||||
type: 'number',
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
],
|
|
||||||
returns: {
|
|
||||||
type: 'object',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
verb: 'POST',
|
|
||||||
path: '/deleteNotification'
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
Self.deleteNotification = async function(ctx, notificationId) {
|
const worker = await models.Worker.findById(workerId, {fields: ['id', 'bossFk']});
|
||||||
const models = Self.app.models;
|
const notificationsAvailables = await models.NotificationSubscription.getList(workerId);
|
||||||
const user = ctx.req.accessToken.userId;
|
const hasAcl = notificationsAvailables.some(function(available) {
|
||||||
const modifiedUser = await getUserToModify(notificationId, null, models);
|
return available.notificationFk === notificationFk;
|
||||||
|
|
||||||
if (user != modifiedUser.id && user != modifiedUser.bossFk)
|
|
||||||
throw new UserError('You dont have permission to modify this user');
|
|
||||||
|
|
||||||
await models.NotificationSubscription.destroyById(notificationId);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getUserToModify(notificationId, userFk, models) {
|
|
||||||
let userToModify = userFk;
|
|
||||||
if (notificationId) {
|
|
||||||
const subscription = await models.NotificationSubscription.findById(notificationId);
|
|
||||||
userToModify = subscription.userFk;
|
|
||||||
}
|
|
||||||
return await models.Worker.findOne({
|
|
||||||
fields: ['id', 'bossFk'],
|
|
||||||
where: {
|
|
||||||
id: userToModify
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!hasAcl || (userId != worker.id && userId != worker.bossFk))
|
||||||
|
throw new UserError('The notification subscription of this worker cant be modified');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,69 +1,126 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('loopback model NotificationSubscription', () => {
|
describe('loopback model NotificationSubscription', () => {
|
||||||
it('should fail to delete a notification if the user is not editing itself or a subordinate', async() => {
|
it('should fail to add a notification subscription if the worker doesnt have ACLs', async() => {
|
||||||
const tx = await models.NotificationSubscription.beginTransaction({});
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
const user = 9;
|
await models.NotificationSubscription.create({notificationFk: 1, userFk: 62}, options);
|
||||||
const notificationSubscriptionId = 2;
|
|
||||||
const ctx = {req: {accessToken: {userId: user}}};
|
|
||||||
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
|
|
||||||
|
|
||||||
expect(error.message).toContain('You dont have permission to modify this user');
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
error = e;
|
error = e;
|
||||||
await tx.rollback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('The notification subscription of this worker cant be modified');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete a notification if the user is editing itself', async() => {
|
it('should fail to add a notification subscription if the user isnt editing itself or subordinate', async() => {
|
||||||
const tx = await models.NotificationSubscription.beginTransaction({});
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx, accessToken: {userId: 1}};
|
||||||
const user = 9;
|
await models.NotificationSubscription.create({notificationFk: 1, userFk: 9}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('The notification subscription of this worker cant be modified');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to delete a notification subscription if the user isnt editing itself or subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
|
const notificationSubscriptionId = 2;
|
||||||
|
await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('The notification subscription of this worker cant be modified');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a notification subscription if the user is editing itself', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
|
await models.NotificationSubscription.create({notificationFk: 2, userFk: 9}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete a notification subscription if the user is editing itself', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
const notificationSubscriptionId = 6;
|
const notificationSubscriptionId = 6;
|
||||||
const ctx = {req: {accessToken: {userId: user}}};
|
await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
|
||||||
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
|
|
||||||
|
|
||||||
const deletedNotification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
expect(deletedNotification).toBeNull();
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
throw e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect(error).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete a notification if the user is editing a subordinate', async() => {
|
it('should add a notification subscription if the user is editing a subordinate', async() => {
|
||||||
const tx = await models.NotificationSubscription.beginTransaction({});
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
const user = 19;
|
await models.NotificationSubscription.create({notificationFk: 1, userFk: 5}, options);
|
||||||
const notificationSubscriptionId = 4;
|
|
||||||
const ctx = {req: {accessToken: {userId: user}}};
|
|
||||||
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
|
|
||||||
|
|
||||||
const deletedNotification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
expect(deletedNotification).toBeNull();
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
throw e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete a notification subscription if the user is editing a subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 19}};
|
||||||
|
const notificationSubscriptionId = 4;
|
||||||
|
await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES
|
||||||
|
('NotificationSubscription', 'getList', 'READ', 'ALLOW', 'ROLE', 'employee');
|
|
@ -290,5 +290,6 @@
|
||||||
"isTaxDataChecked": "Datos comprobados",
|
"isTaxDataChecked": "Datos comprobados",
|
||||||
"comercialId": "Id comercial",
|
"comercialId": "Id comercial",
|
||||||
"comercialName": "Comercial",
|
"comercialName": "Comercial",
|
||||||
"Invalid NIF for VIES": "Invalid NIF for VIES"
|
"Invalid NIF for VIES": "Invalid NIF for VIES",
|
||||||
|
"The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue