From d36e52d5d1c810b7e58d329ce8777ed51c91348a Mon Sep 17 00:00:00 2001 From: DebaucheryLibrarian Date: Sun, 4 Apr 2021 22:52:54 +0200 Subject: [PATCH] Added row level security to alert tables. Added alerts to user query. --- assets/js/fragments.js | 1 + assets/js/users/actions.js | 33 +++++++++++++- migrations/20190325001339_releases.js | 63 +++++++++++++++++++++++---- src/alerts.js | 48 ++++++++++++++++---- src/web/alerts.js | 4 +- 5 files changed, 130 insertions(+), 19 deletions(-) diff --git a/assets/js/fragments.js b/assets/js/fragments.js index af6d6e6f..eadbe707 100644 --- a/assets/js/fragments.js +++ b/assets/js/fragments.js @@ -478,6 +478,7 @@ function getIncludedActors(router) { } export { + actorFields, actorStashesFields, releaseActorsFragment, releaseFields, diff --git a/assets/js/users/actions.js b/assets/js/users/actions.js index 3d9e035c..7f3916a4 100644 --- a/assets/js/users/actions.js +++ b/assets/js/users/actions.js @@ -1,5 +1,5 @@ import { graphql, post, del } from '../api'; -import { releaseFields } from '../fragments'; +import { actorFields, releaseFields } from '../fragments'; import { curateUser } from '../curate'; function initUsersActions(store, _router) { @@ -55,6 +55,37 @@ function initUsersActions(store, _router) { } } } + alerts { + id + notify + email + tags: alertsTags { + tag { + id + name + slug + } + } + actors: alertsActors { + actor { + ${actorFields} + } + } + entity: alertsEntityByAlertId { + entity { + id + name + slug + independent + parent { + id + name + slug + independent + } + } + } + } } } `, { diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js index 978d9fec..1e4eda56 100644 --- a/migrations/20190325001339_releases.js +++ b/migrations/20190325001339_releases.js @@ -1142,6 +1142,7 @@ exports.up = knex => Promise.resolve() table.increments('id'); table.integer('user_id') + .notNullable() .references('id') .inTable('users') .onDelete('cascade'); @@ -1152,11 +1153,6 @@ exports.up = knex => Promise.resolve() table.boolean('email') .defaultTo(false); - table.integer('stash_id') - .references('id') - .inTable('stashes') - .onDelete('cascade'); - table.datetime('created_at') .notNullable() .defaultTo(knex.fn.now()); @@ -1217,6 +1213,7 @@ exports.up = knex => Promise.resolve() table.integer('alert_id') .notNullable() + .unique() .references('id') .inTable('alerts') .onDelete('cascade'); @@ -1226,8 +1223,6 @@ exports.up = knex => Promise.resolve() .references('id') .inTable('entities') .onDelete('cascade'); - - table.unique(['alert_id', 'entity_id']); })) .then(() => knex.schema.createTable('alerts_stashes', (table) => { table.increments('id'); @@ -1244,7 +1239,7 @@ exports.up = knex => Promise.resolve() .inTable('stashes') .onDelete('cascade'); - table.unique(['stash_id', 'entity_id']); + table.unique(['alert_id', 'stash_id']); })) // SEARCH .then(() => { // eslint-disable-line arrow-body-style @@ -1493,6 +1488,58 @@ exports.up = knex => Promise.resolve() WHERE stashes.id = stashes_actors.stash_id AND (stashes.user_id = current_user_id() OR stashes.public) )); + + ALTER TABLE alerts ENABLE ROW LEVEL SECURITY; + ALTER TABLE alerts_tags ENABLE ROW LEVEL SECURITY; + ALTER TABLE alerts_scenes ENABLE ROW LEVEL SECURITY; + ALTER TABLE alerts_actors ENABLE ROW LEVEL SECURITY; + ALTER TABLE alerts_entities ENABLE ROW LEVEL SECURITY; + ALTER TABLE alerts_stashes ENABLE ROW LEVEL SECURITY; + + CREATE POLICY alerts_policy_select ON alerts FOR SELECT USING (alerts.user_id = current_user_id()); + CREATE POLICY alerts_policy_update ON alerts FOR UPDATE USING (alerts.user_id = current_user_id()); + CREATE POLICY alerts_policy_delete ON alerts FOR DELETE USING (alerts.user_id = current_user_id()); + CREATE POLICY alerts_policy_insert ON alerts FOR INSERT WITH CHECK (true); + + CREATE POLICY alerts_policy ON alerts_scenes + USING (EXISTS ( + SELECT * + FROM alerts + WHERE alerts.id = alerts_scenes.alert_id + AND alerts.user_id = current_user_id() + )); + + CREATE POLICY alerts_policy ON alerts_actors + USING (EXISTS ( + SELECT * + FROM alerts + WHERE alerts.id = alerts_actors.alert_id + AND alerts.user_id = current_user_id() + )); + + CREATE POLICY alerts_policy ON alerts_entities + USING (EXISTS ( + SELECT * + FROM alerts + WHERE alerts.id = alerts_entities.alert_id + AND alerts.user_id = current_user_id() + )); + + CREATE POLICY alerts_policy ON alerts_tags + USING (EXISTS ( + SELECT * + FROM alerts + WHERE alerts.id = alerts_tags.alert_id + AND alerts.user_id = current_user_id() + )); + + CREATE POLICY alerts_policy ON alerts_stashes + USING (EXISTS ( + SELECT * + FROM alerts + WHERE alerts.id = alerts_stashes.alert_id + AND alerts.user_id = current_user_id() + )); `, { visitor: knex.raw(config.database.query.user), }); diff --git a/src/alerts.js b/src/alerts.js index 6be20a03..2606ab27 100644 --- a/src/alerts.js +++ b/src/alerts.js @@ -1,16 +1,48 @@ 'use strict'; const knex = require('./knex'); +const { HttpError } = require('./errors'); -async function addAlert(alert, user) { - console.log(alert); - const alertId = await knex('alerts').insert({ - user_id: user.id, - notify: alert.notify, - email: alert.notify, - }); +async function addAlert(alert, sessionUser) { + if (!sessionUser) { + throw new HttpError('You are not authenthicated', 401); + } - console.log(alertId); + if (!alert.actors?.length > 0 && !alert.tags?.length > 0 && !alert.entity) { + throw new HttpError('Alert must contain at least one actor, tag or entity', 400); + } + + const [alertId] = await knex('alerts') + .insert({ + user_id: sessionUser.id, + notify: alert.notify, + email: alert.email, + }) + .returning('id'); + + await Promise.all([ + alert.actors?.length > 0 && knex('alerts_actors') + .insert(alert.actors.map(actorId => ({ + alert_id: alertId, + actor_id: actorId, + }))), + alert.tags?.length > 0 && knex('alerts_tags') + .insert(alert.tags.map(tagId => ({ + alert_id: alertId, + tag_id: tagId, + }))), + alert.stashes?.length > 0 && knex('alerts_stashes') + .insert(alert.stashes.map(stashId => ({ + alert_id: alertId, + stash_id: stashId, + }))), + alert.entity && knex('alerts_entities').insert({ + alert_id: alertId, + entity_id: alert.entity, + }), + ]); + + return alertId; } async function removeAlert(alertId) { diff --git a/src/web/alerts.js b/src/web/alerts.js index 28e08100..97ab1a90 100644 --- a/src/web/alerts.js +++ b/src/web/alerts.js @@ -3,9 +3,9 @@ const { addAlert, removeAlert } = require('../alerts'); async function addAlertApi(req, res) { - const alert = await addAlert(req.body, req.session.user); + const alertId = await addAlert(req.body, req.session.user); - res.send(alert); + res.send({ id: alertId }); } async function removeAlertApi(req, res) {