(core) update read access for exceptional sessions

Summary:
Exceptional sessions had lost full read access to documents; this
restores it.  Exceptional sessions are used for system actions or
while creating documents.

Test Plan: added test

Reviewers: alexmojaki

Reviewed By: alexmojaki

Differential Revision: https://phab.getgrist.com/D2966
This commit is contained in:
Paul Fitzpatrick 2021-08-05 12:07:23 -04:00
parent 4ca47878ca
commit 4a23b964ed

View File

@ -653,6 +653,13 @@ export class GranularAccess implements GranularAccessForBundle {
* access level and any row-level access functions needed.
*/
public async getTableAccess(docSession: OptDocSession, tableId: string): Promise<TablePermissionSetWithContext> {
if (this._hasExceptionalFullAccess(docSession)) {
return {
perms: {read: 'allow', create: 'allow', delete: 'allow', update: 'allow', schemaEdit: 'allow'},
ruleType: 'table',
getMemos() { throw new Error('never needed'); }
};
}
return (await this._getAccess(docSession)).getTableAccess(tableId);
}
@ -665,6 +672,7 @@ export class GranularAccess implements GranularAccessForBundle {
const cursor: ActionCursor = {docSession, action: data, actionIdx: null};
const tableId = getTableId(data);
if (this.getReadPermission(permInfo.getTableAccess(tableId)) === 'mixed') {
const readAccessCheck = this._readAccessCheck(docSession);
await this._filterRowsAndCells(cursor, data, data, readAccessCheck, true);
}
@ -684,7 +692,7 @@ export class GranularAccess implements GranularAccessForBundle {
}
public assertCanRead(ps: PermissionSetWithContext) {
readAccessCheck.throwIfDenied(ps);
accessChecks.fatal.read.get(ps);
}
/**
@ -734,6 +742,18 @@ export class GranularAccess implements GranularAccessForBundle {
}
}
// The AccessCheck for the "read" permission is used enough to merit a shortcut.
// We just need to be careful to retain unfettered access for exceptional sessions.
private _readAccessCheck(docSession: OptDocSession): IAccessCheck {
return this._hasExceptionalFullAccess(docSession) ? dummyAccessCheck : accessChecks.check.read;
}
// Return true for special system sessions or document-creation sessions, where
// unfettered access is appropriate.
private _hasExceptionalFullAccess(docSession: OptDocSession): Boolean {
return docSession.mode === 'system' || docSession.mode === 'nascent';
}
/**
* This filters a message being broadcast to all clients to be appropriate for one
* particular client, if that client may need some material filtered out.
@ -909,6 +929,7 @@ export class GranularAccess implements GranularAccessForBundle {
}
// Return the results, also applying any cell-level access control.
const readAccessCheck = this._readAccessCheck(cursor.docSession);
for (const a of revisedDocActions) {
await this._filterRowsAndCells({...cursor, action: a}, rowsAfter, rowsAfter, readAccessCheck, false);
}
@ -1518,6 +1539,7 @@ export class GranularAccess implements GranularAccessForBundle {
const permInfo = await this._getStepAccess(cursor);
const tableAccess = permInfo.getTableAccess(tableId);
const access = this.getReadPermission(tableAccess);
const readAccessCheck = this._readAccessCheck(cursor.docSession);
const results: DocAction[] = [];
if (access === 'deny') {
// filter out this data.
@ -1636,7 +1658,7 @@ export class GranularAccess implements GranularAccessForBundle {
// TODO: deal with ReplaceTableData, which both deletes and creates rows.
private async _getAccessForActionType(docSession: OptDocSession, a: DocAction,
severity: 'check'|'fatal'): Promise<IAccessCheck> {
if (docSession.mode === 'system' || docSession.mode === 'nascent') {
if (this._hasExceptionalFullAccess(docSession)) {
return dummyAccessCheck;
}
const tableId = getTableId(a);
@ -1821,6 +1843,7 @@ class UserAttributes {
interface IAccessCheck {
get(ps: PermissionSetWithContext): string;
throwIfDenied(ps: PermissionSetWithContext): void;
}
class AccessCheck implements IAccessCheck {
@ -1852,11 +1875,11 @@ export const accessChecks = {
};
// The AccessCheck for the "read" permission is used enough to merit a shortcut.
const readAccessCheck = accessChecks.check.read;
// This AccessCheck allows everything.
const dummyAccessCheck = { get() { return 'allow'; } };
const dummyAccessCheck: IAccessCheck = {
get() { return 'allow'; },
throwIfDenied() {}
};
/**