(core) deal with write access for attachments

Summary:
Attachments are a special case for granular access control. A user is now allowed to read a given attachment if they have read access to a cell containing its id. So when a user writes to a cell in an attachment column, it is important that they can only write the ids of cells to which they have access. This diff allows a user to add an attachment id in a cell if:

  * The user already has access to that a attachment via some existing cell, or
  * The user recently updated the attachment, or
  * The attachment change is from an undo/redo of a previous action attributed to that user

Test Plan: Updated tests

Reviewers: georgegevoian, dsagal

Reviewed By: georgegevoian, dsagal

Differential Revision: https://phab.getgrist.com/D3681
This commit is contained in:
Paul Fitzpatrick
2022-11-15 09:37:48 -05:00
parent 955fdf4ae7
commit ea71312d0e
11 changed files with 344 additions and 86 deletions

View File

@@ -6,6 +6,7 @@ import {
LocalActionBundle,
UserActionBundle
} from 'app/common/ActionBundle';
import {ApplyUAExtendedOptions} from 'app/common/ActiveDocAPI';
import {CALCULATING_USER_ACTIONS, DocAction, getNumRows, UserAction} from 'app/common/DocActions';
import {allToken} from 'app/common/sharing';
import log from 'app/server/lib/log';
@@ -195,15 +196,16 @@ export class Sharing {
const userActions: UserAction[] = [
['ApplyDocActions', action.stored.map(envContent => envContent[1])]
];
return this._doApplyUserActions(action.info[1], userActions, Branch.Shared, null);
return this._doApplyUserActions(action.info[1], userActions, Branch.Shared, null, null);
}
private _doApplyUserActionBundle(action: UserActionBundle, docSession: OptDocSession|null): Promise<UserResult> {
return this._doApplyUserActions(action.info, action.userActions, Branch.Local, docSession);
return this._doApplyUserActions(action.info, action.userActions, Branch.Local, docSession, action.options || null);
}
private async _doApplyUserActions(info: ActionInfo, userActions: UserAction[],
branch: Branch, docSession: OptDocSession|null): Promise<UserResult> {
branch: Branch, docSession: OptDocSession|null,
options: ApplyUAExtendedOptions|null): Promise<UserResult> {
const client = docSession && docSession.client;
if (docSession?.linkId) {
@@ -211,7 +213,7 @@ export class Sharing {
}
const {sandboxActionBundle, undo, accessControl} =
await this._modificationLock.runExclusive(() => this._applyActionsToDataEngine(docSession, userActions));
await this._modificationLock.runExclusive(() => this._applyActionsToDataEngine(docSession, userActions, options));
try {
@@ -389,7 +391,8 @@ export class Sharing {
shortDesc(envAction[1])));
}
private async _applyActionsToDataEngine(docSession: OptDocSession|null, userActions: UserAction[]) {
private async _applyActionsToDataEngine(docSession: OptDocSession|null, userActions: UserAction[],
options: ApplyUAExtendedOptions|null) {
const sandboxActionBundle = await this._activeDoc.applyActionsToDataEngine(docSession, userActions);
const undo = getEnvContent(sandboxActionBundle.undo);
const docActions = getEnvContent(sandboxActionBundle.stored).concat(
@@ -397,7 +400,8 @@ export class Sharing {
const isDirect = getEnvContent(sandboxActionBundle.direct);
const accessControl = this._activeDoc.getGranularAccessForBundle(
docSession || makeExceptionalDocSession('share'), docActions, undo, userActions, isDirect
docSession || makeExceptionalDocSession('share'), docActions, undo, userActions, isDirect,
options
);
try {
// TODO: see if any of the code paths that have no docSession are relevant outside