(core) implement cleaner row-level access control for outgoing messages

Summary:
This implements row-level access control for outgoing messages, replacing the document reloading placeholder that was there before.

 * Prior to broadcasting messages, GranularAccess is notified of actions+undo.
 * While broadcasting messages to different sessions, if we find we need row level access control information, rows before and after the change are reconstructed.
 * Messages are rewritten if rows that were previously forbidden are now allowed, and vice versa.

The diff is somewhat under-tested and under-optimized. Next step would be to implement row-level access control for incoming actions, which may result in some rejiggering of the code from this diff to avoid duplication of effort under some conditions.

Test Plan: added test

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D2670
This commit is contained in:
Paul Fitzpatrick
2020-11-30 10:50:00 -05:00
parent c1c17bf54e
commit 0e2deecc55
8 changed files with 433 additions and 61 deletions

View File

@@ -222,6 +222,7 @@ export class Sharing {
(branch === Branch.Shared ? this._actionHistory.getNextHubActionNum() :
this._actionHistory.getNextLocalActionNum());
const undo = getEnvContent(sandboxActionBundle.undo);
const localActionBundle: LocalActionBundle = {
actionNum,
// The ActionInfo should go into the envelope that includes all recipients.
@@ -236,6 +237,9 @@ export class Sharing {
};
this._logActionBundle(`doApplyUserActions (${Branch[branch]})`, localActionBundle);
const docActions = getEnvContent(localActionBundle.stored).concat(
getEnvContent(localActionBundle.calc));
// TODO Note that the sandbox may produce actions which are not addressed to us (e.g. when we
// have EDIT permission without VIEW). These are not sent to the browser or the database. But
// today they are reflected in the sandbox. Should we (or the sandbox) immediately undo the
@@ -281,11 +285,15 @@ export class Sharing {
// date and other changes from external values may count as internal.
internal: isCalculate,
});
await this._activeDoc.broadcastDocUpdate(client || null, 'docUserAction', {
actionGroup,
docActions: getEnvContent(localActionBundle.stored).concat(
getEnvContent(localActionBundle.calc))
});
await this._activeDoc.beforeBroadcast(docActions, undo);
try {
await this._activeDoc.broadcastDocUpdate(client || null, 'docUserAction', {
actionGroup,
docActions,
});
} finally {
await this._activeDoc.afterBroadcast();
}
return {
actionNum: localActionBundle.actionNum,
retValues: sandboxActionBundle.retValues,