From 3a0ec7b10337e77f275ac4248150b80c0b273a47 Mon Sep 17 00:00:00 2001 From: Paul Fitzpatrick Date: Wed, 12 May 2021 15:43:43 -0400 Subject: [PATCH] (core) be less fussy about rec/newRec distinction for creates/deletions Summary: For row creations and deletions, treat `rec` and `newRec` variables as identical. This simplifies writing a single rule that controls multiple permissions. Test Plan: added test Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2812 --- app/server/lib/GranularAccess.ts | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/app/server/lib/GranularAccess.ts b/app/server/lib/GranularAccess.ts index d3363e3d..3419c976 100644 --- a/app/server/lib/GranularAccess.ts +++ b/app/server/lib/GranularAccess.ts @@ -44,6 +44,14 @@ function isDataAction(a: UserAction): a is DataAction { return ACTION_WITH_TABLE_ID.has(String(a[0])); } +function isAddRecordAction(a: DataAction): boolean { + return ['AddRecord', 'BulkAddRecord'].includes(a[0]); +} + +function isRemoveRecordAction(a: DataAction): boolean { + return ['RemoveRecord', 'BulkRemoveRecord'].includes(a[0]); +} + // Check if a tableId is that of an ACL table. Currently just _grist_ACLRules and // _grist_ACLResources are accepted. function isAclTable(tableId: string): boolean { @@ -820,8 +828,19 @@ export class GranularAccess implements GranularAccessForBundle { return []; } - const rec = new RecordView(rowsBefore, undefined); - const newRec = new RecordView(rowsAfter, undefined); + // For user convenience, for creations and deletions we equate rec and newRec. + // This makes writing rules that control multiple permissions easier to write in + // practice. + let rowsRec = rowsBefore; + let rowsNewRec = rowsAfter; + if (isAddRecordAction(action)) { + rowsRec = rowsAfter; + } else if (isRemoveRecordAction(action)) { + rowsNewRec = rowsBefore; + } + + const rec = new RecordView(rowsRec, undefined); + const newRec = new RecordView(rowsNewRec, undefined); const input: AclMatchInput = {user: await this._getUser(docSession), rec, newRec}; const [, tableId, , colValues] = action; @@ -840,10 +859,12 @@ export class GranularAccess implements GranularAccessForBundle { // These map an index of a row in the action to its index in rowsBefore and in rowsAfter. let getRecIndex: (idx: number) => number|undefined = (idx) => idx; let getNewRecIndex: (idx: number) => number|undefined = (idx) => idx; - if (action !== rowsBefore) { - const recIndexes = new Map(rowsBefore[2].map((rowId, idx) => [rowId, idx])); + if (action !== rowsRec) { + const recIndexes = new Map(rowsRec[2].map((rowId, idx) => [rowId, idx])); getRecIndex = (idx) => recIndexes.get(rowIds[idx]); - const newRecIndexes = new Map(rowsAfter[2].map((rowId, idx) => [rowId, idx])); + } + if (action !== rowsNewRec) { + const newRecIndexes = new Map(rowsNewRec[2].map((rowId, idx) => [rowId, idx])); getNewRecIndex = (idx) => newRecIndexes.get(rowIds[idx]); }