gristlabs_grist-core/app/client/widgets/NewBaseEditor.ts
Paul Fitzpatrick f91f45b26d (core) support granular read access for attachments
Summary:
When a user requests to read the contents of an attachment, only allow the request if there exists a cell in an attachment column that contains the attachment and which they have read access to.

This does not cover:
 * Granular write access for attachments. In particular, a user who can write to any attachment column should be considered to have full read access to all attachment columns, currently.
 * Access control of attachment metadata such as name and format.

The implementation uses a sql query that requires a scan, and some notes on how this could be optimized in future. The web client was updated to specify the cell to check for access, and performance seemed fine in casual testing on a doc with 1000s of attachments. I'm not sure how performance would hold up as the set of access rules grows as well.

Test Plan: added tests

Reviewers: alexmojaki

Reviewed By: alexmojaki

Differential Revision: https://phab.getgrist.com/D3490
2022-07-07 07:22:02 -04:00

112 lines
3.7 KiB
TypeScript

/**
* NewBaseEditor is equivalent to BaseEditor for outside code, but is in typescript, and
* so is friendlier and clearer to derive TypeScript classes from.
*/
import {GristDoc} from 'app/client/components/GristDoc';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
import {CellValue} from "app/common/DocActions";
import {Disposable, IDisposableOwner, Observable} from 'grainjs';
export interface IEditorCommandGroup {
fieldEditCancel: () => void;
fieldEditSaveHere: () => void;
[cmd: string]: () => void;
}
export interface Options {
gristDoc: GristDoc;
field: ViewFieldRec;
cellValue: CellValue;
rowId: number;
formulaError?: Observable<CellValue>;
editValue?: string;
cursorPos: number;
commands: IEditorCommandGroup;
state?: any;
readonly: boolean;
}
/**
* Required parameters:
* @param {RowModel} options.field: ViewSectionField (i.e. column) being edited.
* @param {String} options.cellValue: The value in the underlying cell being edited.
* @param {String} options.editValue: String to be edited, or undefined to use cellValue.
* @param {Number} options.cursorPos: The initial position where to place the cursor.
* @param {Object} options.commands: Object mapping command names to functions, to enable as part
* of the command group that should be activated while the editor exists.
*/
export abstract class NewBaseEditor extends Disposable {
/**
* Override the create() method to allow the parameters of create() expected by old-style
* Editors and provided by FieldBuilder. TODO: remove this method once all editors have been
* updated to new-style Disposables.
*/
public static create<Opt extends Options>(owner: IDisposableOwner|null, options: Opt): NewBaseEditor;
public static create(options: Options): NewBaseEditor;
public static create(ownerOrOptions: any, options?: any): NewBaseEditor {
return options ?
Disposable.create.call(this as any, ownerOrOptions, options) :
Disposable.create.call(this as any, null, ownerOrOptions);
}
/**
* Check if the typed-in value should change the cell without opening the editor, and if so,
* returns the value to save. E.g. on typing " ", CheckBoxEditor toggles value without opening.
*/
public static skipEditor(typedVal: string|undefined, origVal: CellValue): CellValue|undefined {
return undefined;
}
/**
* Check if editor supports readonly mode (default: true)
*/
public static supportsReadonly(): boolean {
return true;
}
/**
* Current state of the editor. Optional, not all editors will report theirs current state.
*/
public editorState?: Observable<any>;
constructor(protected options: Options) {
super();
}
/**
* Called after the editor is instantiated to attach its DOM to the page.
* - cellElem: The element representing the cell that this editor should match
* in size and position. Used by derived classes, e.g. to construct an EditorPlacement object.
*/
public abstract attach(cellElem: Element): void;
/**
* Returns DOM container with the editor, typically present and attached after attach() has been
* called.
*/
public getDom(): HTMLElement|null { return null; }
/**
* Called to get the value to save back to the cell.
*/
public abstract getCellValue(): CellValue;
/**
* Used if an editor needs perform any actions before a save
*/
public prepForSave(): void | Promise<void> {
// No-op by default.
}
/**
* Called to get the text in the editor, used when switching between editing data and formula.
*/
public abstract getTextValue(): string;
/**
* Called to get the position of the cursor in the editor. Used when switching between editing
* data and formula.
*/
public abstract getCursorPos(): number;
}