UNPKG

@deep-foundation/deeplinks

Version:

[![npm](https://img.shields.io/npm/v/@deep-foundation/deeplinks.svg)](https://www.npmjs.com/package/@deep-foundation/deeplinks) [![Gitpod](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/deep-fo

426 lines (413 loc) 17.7 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { generateApolloClient } from '@deep-foundation/hasura/client.js'; import { sql } from '@deep-foundation/hasura/sql.js'; import Debug from 'debug'; import { DeepClient, _ids } from '../imports/client.js'; import { permissions } from '../imports/permission.js'; import { api, TABLE_NAME as LINKS_TABLE_NAME } from './1616701513782-links.js'; import { MP_TABLE_NAME, TREE_TABLE_NAME } from './1621815803572-materialized-path.js'; import { SELECTORS_TABLE_NAME } from './1622421760258-selectors.js'; import { CAN_TABLE_NAME } from './1622421760259-can.js'; const debug = Debug('deeplinks:migrations:permissions'); const log = debug.extend('log'); const error = debug.extend('error'); export const TABLE_NAME = 'links'; const client = generateApolloClient({ path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), secret: process.env.MIGRATIONS_HASURA_SECRET, }); const deep = new DeepClient({ apolloClient: client, }); export const isAdminBoolExp = (subjectId = 'X-Hasura-User-Id') => __awaiter(void 0, void 0, void 0, function* () { return ({ "_table": { "schema": "public", "name": "can" }, _where: { object_id: { _eq: subjectId }, subject_id: { _eq: subjectId }, action_id: { _eq: deep.idLocal('@deep-foundation/core', 'AllowAdmin') }, }, }); }); export const linksPermissions = (self, subjectId = 'X-Hasura-User-Id', role) => __awaiter(void 0, void 0, void 0, function* () { return ({ role, select: { can_object: { action_id: { _eq: deep.idLocal('@deep-foundation/core', 'AllowSelect') }, subject_id: { _eq: subjectId }, }, }, insert: { type: { can_object: { action_id: { _eq: deep.idLocal('@deep-foundation/core', 'AllowInsert') }, subject_id: { _eq: subjectId }, }, }, }, update: { can_object: { action_id: { _eq: deep.idLocal('@deep-foundation/core', 'AllowUpdate') }, subject_id: { _eq: subjectId }, }, }, delete: { can_object: { action_id: { _eq: deep.idLocal('@deep-foundation/core', 'AllowDelete') }, subject_id: { _eq: subjectId }, }, }, columns: ['id', 'from_id', 'to_id', 'type_id'], computed_fields: ['value'], }); }); export const up = () => __awaiter(void 0, void 0, void 0, function* () { var _a, _b; log('up'); log('hasura permissions'); yield permissions(api, MP_TABLE_NAME, { role: 'link', select: { _and: [ { item: (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link')).select, }, { path_item: (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link')).select, }, ] }, insert: { id: { _is_null: true } }, update: { id: { _is_null: true } }, delete: { id: { _is_null: true } }, columns: '*', computed_fields: [], }); yield permissions(api, MP_TABLE_NAME, { role: 'undefined', select: { id: { _is_null: true }, }, insert: { id: { _is_null: true } }, update: { id: { _is_null: true } }, delete: { id: { _is_null: true } }, columns: '*', computed_fields: [], }); yield permissions(api, TREE_TABLE_NAME, { role: 'link', select: { _and: [ { link: (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link')).select, }, { parent: (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link')).select, }, ] }, insert: { id: { _is_null: true } }, update: { id: { _is_null: true } }, delete: { id: { _is_null: true } }, columns: '*', computed_fields: [], }); yield permissions(api, TREE_TABLE_NAME, { role: 'undefined', select: { id: { _is_null: true }, }, insert: { id: { _is_null: true } }, update: { id: { _is_null: true } }, delete: { id: { _is_null: true } }, columns: '*', computed_fields: [], }); yield permissions(api, CAN_TABLE_NAME, { role: 'link', select: {}, insert: {}, update: {}, delete: {}, columns: '*', computed_fields: [], }); yield permissions(api, CAN_TABLE_NAME, { role: 'undefined', select: { object_id: { _is_null: true }, subject_id: { _is_null: true }, action_id: { _is_null: true }, rule_id: { _is_null: true }, }, insert: {}, update: {}, delete: {}, columns: '*', computed_fields: [], }); yield permissions(api, SELECTORS_TABLE_NAME, { role: 'link', select: { _and: [ { selector: (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link')).select, }, { item: (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link')).select, }, ] }, insert: {}, update: {}, delete: {}, columns: '*', computed_fields: [], }); yield permissions(api, SELECTORS_TABLE_NAME, { role: 'undefined', select: { selector_id: { _is_null: true }, item_id: { _is_null: true }, }, insert: {}, update: {}, delete: {}, columns: '*', computed_fields: [], }); yield (() => __awaiter(void 0, void 0, void 0, function* () { yield permissions(api, LINKS_TABLE_NAME, (yield linksPermissions(['$', 'id'], 'X-Hasura-User-Id', 'link'))); const valuesPermissions = Object.assign(Object.assign({}, (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link'))), { select: { link: (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link')).select, }, insert: { link: (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link')).insert, }, update: { link: (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link')).update, }, delete: { link: (yield linksPermissions(['$', 'link_id'], 'X-Hasura-User-Id', 'link')).delete, }, columns: ['id', 'link_id', 'value'], computed_fields: [] }); yield permissions(api, 'strings', valuesPermissions); yield permissions(api, 'numbers', valuesPermissions); yield permissions(api, 'objects', valuesPermissions); }))(); yield (() => __awaiter(void 0, void 0, void 0, function* () { var _c, _d, _e, _f, _g, _h; yield permissions(api, LINKS_TABLE_NAME, (yield linksPermissions(['$', 'id'], (_c = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _c === void 0 ? void 0 : _c.Any, 'undefined'))); const valuesPermissions = Object.assign(Object.assign({}, (yield linksPermissions(['$', 'link_id'], (_d = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _d === void 0 ? void 0 : _d.Any, 'undefined'))), { select: { link: (yield linksPermissions(['$', 'link_id'], (_e = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _e === void 0 ? void 0 : _e.Any, 'undefined')).select, }, insert: { link: (yield linksPermissions(['$', 'link_id'], (_f = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _f === void 0 ? void 0 : _f.Any, 'undefined')).insert, }, update: { link: (yield linksPermissions(['$', 'link_id'], (_g = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _g === void 0 ? void 0 : _g.Any, 'undefined')).update, }, delete: { link: (yield linksPermissions(['$', 'link_id'], (_h = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _h === void 0 ? void 0 : _h.Any, 'undefined')).delete, }, columns: ['id', 'link_id', 'value'], computed_fields: [] }); yield permissions(api, 'strings', valuesPermissions); yield permissions(api, 'numbers', valuesPermissions); yield permissions(api, 'objects', valuesPermissions); }))(); log('postgresql triggers'); log('insert'); yield api.sql(sql ` CREATE OR REPLACE FUNCTION public.${TABLE_NAME}__permissions__insert_links__function() RETURNS trigger LANGUAGE plpgsql AS $function$ DECLARE boolExp RECORD; typeLink RECORD; fromLink RECORD; toLink RECORD; sqlResult BOOL; session_variables json; user_id bigint; userRole TEXT; foundBoolExpError RECORD; foundNotErroredBoolExp BOOL = FALSE; BEGIN session_variables := current_setting('hasura.user', 't'); IF session_variables IS NULL THEN session_variables := ('{ "x-hasura-role": "link", "x-hasura-user-id": "${deep.idLocal('@deep-foundation/core', 'Any')}" }')::json; END IF; user_id := (session_variables::json->>'x-hasura-user-id')::bigint; userRole := (session_variables::json->>'x-hasura-role')::text; IF (NEW."from_id" != NEW."to_id" AND (NEW."from_id" = 0 OR NEW."to_id" = 0)) THEN RAISE EXCEPTION 'Particular links is not allowed id: %, from: %, to: %, type: %.', NEW."id", NEW."from_id", NEW."to_id", NEW."type_id"; END IF; SELECT t.* into typeLink FROM "${TABLE_NAME}" as t WHERE t."id" = NEW."type_id"; IF (typeLink IS NULL AND NOT (NEW."type_id" = 0 AND userRole = 'admin')) THEN RAISE EXCEPTION 'Type % not found.', NEW."type_id"; END IF; SELECT t.* into fromLink FROM "${TABLE_NAME}" as t WHERE t."id" = NEW."from_id"; SELECT t.* into toLink FROM "${TABLE_NAME}" as t WHERE t."id" = NEW."to_id"; IF (NEW."from_id" != 0 AND NEW."to_id" != 0) THEN IF (typeLink."from_id" != ${(_a = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _a === void 0 ? void 0 : _a.Any} AND typeLink."from_id" != fromLink."type_id") THEN RAISE EXCEPTION 'Type conflict link: { id: %, type: %, from: %, to: % } expected type: { type: %, from: %, to: % } received type: { type: %, from: %, to: % }', NEW."id", NEW."type_id", NEW."from_id", NEW."to_id", typeLink."id", typeLink."from_id", typeLink."to_id", typeLink."id", fromLink."type_id", toLink."type_id" ; END IF; IF (typeLink."to_id" != ${(_b = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _b === void 0 ? void 0 : _b.Any} AND typeLink."to_id" != toLink."type_id") THEN RAISE EXCEPTION 'Type conflict link: { id: %, type: %, from: %, to: % } expected type: { type: %, from: %, to: % } received type: { type: %, from: %, to: % }', NEW."id", NEW."type_id", NEW."from_id", NEW."to_id", typeLink."id", typeLink."from_id", typeLink."to_id", typeLink."id", fromLink."type_id", toLink."type_id" ; END IF; END IF; IF user_id IS NOT NULL AND userRole = 'link' THEN FOR boolExp IN ( SELECT sr.* FROM "${CAN_TABLE_NAME}" as can, "${TABLE_NAME}" as ro, "${SELECTORS_TABLE_NAME}" as sr WHERE can."object_id" = NEW."id" AND can."subject_id" = user_id AND can."action_id" = ${deep.idLocal('@deep-foundation/core', 'AllowInsert')} AND ro."type_id" = ${deep.idLocal('@deep-foundation/core', 'RuleObject')} AND ro."from_id" = can."rule_id" AND sr."selector_id" = ro."to_id" AND sr."item_id" = NEW."type_id" AND sr."query_id" != 0 ) LOOP SELECT INTO sqlResult bool_exp_execute(NEW.id, boolExp."query_id", user_id); IF (sqlResult = FALSE) THEN foundBoolExpError := boolExp; END IF; IF (sqlResult IS NULL) THEN RAISE EXCEPTION 'insert % rejected because selector_id: % query_id: % user_id: % return null', json_agg(NEW), boolExp."selector_id", boolExp."query_id", user_id; END IF; IF (sqlResult = TRUE) THEN foundNotErroredBoolExp := TRUE; END IF; END LOOP; END IF; IF foundBoolExpError IS NOT NULL AND foundNotErroredBoolExp = FALSE THEN RAISE EXCEPTION 'insert % rejected because selector_id: % query_id: % user_id: % return false', json_agg(NEW), foundBoolExpError."selector_id", foundBoolExpError."query_id", user_id; END IF; RETURN NEW; END; $function$; CREATE TRIGGER ${TABLE_NAME}__permissions__insert_links__trigger AFTER INSERT ON "${TABLE_NAME}" FOR EACH ROW EXECUTE PROCEDURE ${TABLE_NAME}__permissions__insert_links__function(); `); log('delete'); yield api.sql(sql ` CREATE OR REPLACE FUNCTION public.${TABLE_NAME}__permissions__delete_links__function() RETURNS trigger LANGUAGE plpgsql AS $function$ DECLARE boolExp RECORD; sqlResult BOOL; session_variables json; user_id bigint; userRole TEXT; foundBoolExpError RECORD; foundNotErroredBoolExp BOOL = FALSE; BEGIN session_variables := current_setting('hasura.user', 't'); IF session_variables IS NULL THEN session_variables := ('{ "x-hasura-role": "link", "x-hasura-user-id": "${deep.idLocal('@deep-foundation/core', 'Any')}" }')::json; END IF; user_id := (session_variables::json->>'x-hasura-user-id')::bigint; userRole := (session_variables::json->>'x-hasura-role')::text; IF user_id IS NOT NULL AND userRole = 'link' THEN FOR boolExp IN ( SELECT sr.* FROM "${CAN_TABLE_NAME}" as can JOIN "${SELECTORS_TABLE_NAME}" as sr ON sr."selector_id" = can."object_selector_id" WHERE can."object_id" = OLD."id" AND can."subject_id" = user_id AND can."action_id" = ${deep.idLocal('@deep-foundation/core', 'AllowDelete')} AND sr."query_id" IS NOT NULL AND sr."query_id" != 0 ) LOOP SELECT INTO sqlResult bool_exp_execute(OLD.id, boolExp."query_id", user_id); IF (sqlResult = FALSE) THEN foundBoolExpError := boolExp; END IF; IF (sqlResult IS NULL) THEN RAISE EXCEPTION 'delete % rejected because selector_id: % query_id: % user_id: % return null', json_agg(OLD), boolExp."selector_id", boolExp."query_id", user_id; END IF; IF (sqlResult = TRUE) THEN foundNotErroredBoolExp := TRUE; END IF; END LOOP; END IF; IF foundBoolExpError IS NOT NULL AND foundNotErroredBoolExp = FALSE THEN RAISE EXCEPTION 'delete % rejected because selector_id: % query_id: % user_id: % return false', json_agg(OLD), foundBoolExpError."selector_id", foundBoolExpError."query_id", user_id; END IF; RETURN OLD; END; $function$; CREATE TRIGGER ${TABLE_NAME}__permissions__delete_links__trigger BEFORE DELETE ON "${TABLE_NAME}" FOR EACH ROW EXECUTE PROCEDURE ${TABLE_NAME}__permissions__delete_links__function(); `); }); export const down = () => __awaiter(void 0, void 0, void 0, function* () { log('down'); log('insert'); yield api.sql(sql ` DROP TRIGGER IF EXISTS ${TABLE_NAME}__permissions__insert_links__trigger ON ${TABLE_NAME} CASCADE; DROP FUNCTION IF EXISTS ${TABLE_NAME}__permissions__insert_links__function() CASCADE; `); log('delete'); yield api.sql(sql ` DROP TRIGGER IF EXISTS ${TABLE_NAME}__permissions__delete_links__trigger ON ${TABLE_NAME} CASCADE; DROP FUNCTION IF EXISTS ${TABLE_NAME}__permissions__delete_links__function() CASCADE; `); }); //# sourceMappingURL=1622421760260-permissions.js.map