gristlabs_grist-core/app/common/GranularAccessClause.ts
Dmitry S d1c1416d78 (core) Add rules to eslint to better match our coding conventions.
Summary:
We used tslint earlier, and on switching to eslint, some rules were not
transfered. This moves more rules over, for consistent conventions or helpful
warnings.

- Name private members with a leading underscore.
- Prefer interface over a type alias.
- Use consistent spacing around ':' in type annotations.
- Use consistent spacing around braces of code blocks.
- Use semicolons consistently at the ends of statements.
- Use braces around even one-liner blocks, like conditionals and loops.
- Warn about shadowed variables.

Test Plan: Fixed all new warnings. Should be no behavior changes in code.

Reviewers: paulfitz

Reviewed By: paulfitz

Differential Revision: https://phab.getgrist.com/D2831
2021-05-24 12:56:18 -04:00

118 lines
3.6 KiB
TypeScript

import { PartialPermissionSet } from 'app/common/ACLPermissions';
import { CellValue, RowRecord } from 'app/common/DocActions';
export interface RuleSet {
tableId: '*' | string;
colIds: '*' | string[];
// The default permissions for this resource, if set, are represented by a RulePart with
// aclFormula of "", which must be the last element of body.
body: RulePart[];
}
export interface RulePart {
origRecord?: RowRecord; // Original record used to create this RulePart.
aclFormula: string;
permissions: PartialPermissionSet;
permissionsText: string; // The text version of PermissionSet, as stored.
// Compiled version of aclFormula.
matchFunc?: AclMatchFunc;
// Optional memo, currently extracted from comment in formula.
memo?: string;
}
// Light wrapper for reading records or user attributes.
export interface InfoView {
get(key: string): CellValue;
toJSON(): {[key: string]: any};
}
// As InfoView, but also supporting writing.
export interface InfoEditor {
get(key: string): CellValue;
set(key: string, val: CellValue): this;
toJSON(): {[key: string]: any};
}
// Represents user info, which may include properties which are themselves RowRecords.
export type UserInfo = Record<string, CellValue|InfoView|Record<string, string>>;
/**
* Input into the AclMatchFunc. Compiled formulas evaluate AclMatchInput to produce a boolean.
*/
export interface AclMatchInput {
user: UserInfo;
rec?: InfoView;
newRec?: InfoView;
}
/**
* The actual boolean function that can evaluate a request. The result of compiling ParsedAclFormula.
*/
export type AclMatchFunc = (input: AclMatchInput) => boolean;
/**
* Representation of a parsed ACL formula.
*/
type PrimitiveCellValue = number|string|boolean|null;
export type ParsedAclFormula = [string, ...Array<ParsedAclFormula|PrimitiveCellValue>];
/**
* Observations about a formula.
*/
export interface FormulaProperties {
hasRecOrNewRec?: boolean;
usedColIds?: string[];
}
export interface UserAttributeRule {
origRecord?: RowRecord; // Original record used to create this UserAttributeRule.
name: string; // Should be unique among UserAttributeRules.
tableId: string; // Table in which to look up an existing attribute.
lookupColId: string; // Column in tableId in which to do the lookup.
charId: string; // Attribute to look up, possibly a path. E.g. 'Email' or 'office.city'.
}
/**
* Check some key facts about the formula.
*/
export function getFormulaProperties(formula: ParsedAclFormula) {
const result: FormulaProperties = {};
if (usesRec(formula)) { result.hasRecOrNewRec = true; }
const colIds = new Set<string>();
collectRecColIds(formula, colIds);
result.usedColIds = Array.from(colIds);
return result;
}
/**
* Check whether a formula mentions `rec` or `newRec`.
*/
export function usesRec(formula: ParsedAclFormula): boolean {
if (!Array.isArray(formula)) { throw new Error('expected a list'); }
if (isRecOrNewRec(formula)) {
return true;
}
return formula.some(el => {
if (!Array.isArray(el)) { return false; }
return usesRec(el);
});
}
function isRecOrNewRec(formula: ParsedAclFormula|PrimitiveCellValue): boolean {
return Array.isArray(formula) &&
formula[0] === 'Name' &&
(formula[1] === 'rec' || formula[1] === 'newRec');
}
function collectRecColIds(formula: ParsedAclFormula, colIds: Set<string>): void {
if (!Array.isArray(formula)) { throw new Error('expected a list'); }
if (formula[0] === 'Attr' && isRecOrNewRec(formula[1])) {
const colId = formula[2];
colIds.add(String(colId));
return;
}
formula.forEach(el => Array.isArray(el) && collectRecColIds(el, colIds));
}