mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(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:
@@ -82,7 +82,7 @@ const SCHEMA_ACTIONS = new Set(['AddTable', 'RemoveTable', 'RenameTable', 'AddCo
|
||||
/**
|
||||
* Determines whether a given action is a schema action or not.
|
||||
*/
|
||||
export function isSchemaAction(action: DocAction): boolean {
|
||||
export function isSchemaAction(action: DocAction): action is AddTable | RemoveTable | RenameTable | AddColumn | RemoveColumn | RenameColumn | ModifyColumn {
|
||||
return SCHEMA_ACTIONS.has(action[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,14 @@ type FetchTableFunc = (tableId: string) => Promise<TableDataAction>;
|
||||
export class DocData extends ActionDispatcher {
|
||||
private _tables: Map<string, TableData> = new Map();
|
||||
|
||||
constructor(private _fetchTableFunc: FetchTableFunc, metaTableData: {[tableId: string]: TableDataAction}) {
|
||||
/**
|
||||
* If metaTableData is not supplied, then any tables needed should be loaded manually,
|
||||
* using syncTable(). All column types will be set to Any, which will affect default
|
||||
* values.
|
||||
*/
|
||||
constructor(private _fetchTableFunc: FetchTableFunc, metaTableData: {[tableId: string]: TableDataAction} | null) {
|
||||
super();
|
||||
if (metaTableData === null) { return; }
|
||||
// Create all meta tables, and populate data we already have.
|
||||
for (const tableId in schema) {
|
||||
if (schema.hasOwnProperty(tableId)) {
|
||||
@@ -67,6 +73,17 @@ export class DocData extends ActionDispatcher {
|
||||
return (!table.isLoaded || force) ? table.fetchData(this._fetchTableFunc) : Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the data for tableId unconditionally, and without knowledge of its metadata.
|
||||
* Columns will be assumed to have type 'Any'.
|
||||
*/
|
||||
public async syncTable(tableId: string): Promise<void> {
|
||||
const tableData = await this._fetchTableFunc(tableId);
|
||||
const colTypes = fromPairs(Object.keys(tableData[3]).map(c => [c, 'Any']));
|
||||
colTypes.id = 'Any';
|
||||
this._tables.set(tableId, this.createTableData(tableId, tableData, colTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an action received from the server, by forwarding it to the appropriate TableData
|
||||
* object.
|
||||
|
||||
@@ -304,6 +304,7 @@ export interface DocAPI {
|
||||
getRows(tableId: string): Promise<TableColValues>;
|
||||
updateRows(tableId: string, changes: TableColValues): Promise<number[]>;
|
||||
addRows(tableId: string, additions: BulkColValues): Promise<number[]>;
|
||||
removeRows(tableId: string, removals: number[]): Promise<number[]>;
|
||||
replace(source: DocReplacementOptions): Promise<void>;
|
||||
getSnapshots(): Promise<DocSnapshots>;
|
||||
forceReload(): Promise<void>;
|
||||
@@ -690,6 +691,13 @@ export class DocAPIImpl extends BaseAPI implements DocAPI {
|
||||
});
|
||||
}
|
||||
|
||||
public async removeRows(tableId: string, removals: number[]): Promise<number[]> {
|
||||
return this.requestJson(`${this._url}/tables/${tableId}/data/delete`, {
|
||||
body: JSON.stringify(removals),
|
||||
method: 'POST'
|
||||
});
|
||||
}
|
||||
|
||||
public async replace(source: DocReplacementOptions): Promise<void> {
|
||||
return this.requestJson(`${this._url}/replace`, {
|
||||
body: JSON.stringify(source),
|
||||
|
||||
Reference in New Issue
Block a user