mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) implement a safe mode for opening documents with rule problems
Summary: Adds an "enter safe mode" option and explanation in modal that appears when a document fails to load, if user is owner. If "enter safe mode" is selected, document is reloaded on server in a special mode. Currently, the only difference is that if the acl rules fail to load, they are replaced with a fallback that grants full access to owners and no access to anyone else. An extra tag is shown to mark the document as safe mode, with an "x" for cancelling safe mode. There are other ways a document could fail to load than just acl rules, so this is just a start. Test Plan: added test Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2686
This commit is contained in:
@@ -30,6 +30,24 @@ const DEFAULT_RULE_SET: RuleSet = {
|
||||
}],
|
||||
};
|
||||
|
||||
// If the user-created rules become dysfunctional, we can swap in this emergency set.
|
||||
// It grants full access to owners, and no access to anyone else.
|
||||
const EMERGENCY_RULE_SET: RuleSet = {
|
||||
tableId: '*',
|
||||
colIds: '*',
|
||||
body: [{
|
||||
aclFormula: "user.Access in ['owners']",
|
||||
matchFunc: (input) => ['owners'].includes(String(input.user.Access)),
|
||||
permissions: parsePermissions('all'),
|
||||
permissionsText: 'all',
|
||||
}, {
|
||||
aclFormula: "",
|
||||
matchFunc: defaultMatchFunc,
|
||||
permissions: parsePermissions('none'),
|
||||
permissionsText: 'none',
|
||||
}],
|
||||
};
|
||||
|
||||
export class ACLRuleCollection {
|
||||
// In the absence of rules, some checks are skipped. For now this is important to maintain all
|
||||
// existing behavior. TODO should make sure checking access against default rules is equivalent
|
||||
@@ -54,6 +72,10 @@ export class ACLRuleCollection {
|
||||
// Maps name to the corresponding UserAttributeRule.
|
||||
private _userAttributeRules = new Map<string, UserAttributeRule>();
|
||||
|
||||
// Store error if one occurs while reading rules. Rules are replaced with emergency rules
|
||||
// in this case.
|
||||
public ruleError: Error|undefined;
|
||||
|
||||
// Whether there are ANY user-defined rules.
|
||||
public haveRules(): boolean {
|
||||
return this._haveRules;
|
||||
@@ -93,7 +115,7 @@ export class ACLRuleCollection {
|
||||
* Update granular access from DocData.
|
||||
*/
|
||||
public async update(docData: DocData, options: ReadAclOptions) {
|
||||
const {ruleSets, userAttributes} = readAclRules(docData, options);
|
||||
const {ruleSets, userAttributes} = this._safeReadAclRules(docData, options);
|
||||
|
||||
// Build a map of user characteristics rules.
|
||||
const userAttributeMap = new Map<string, UserAttributeRule>();
|
||||
@@ -143,6 +165,16 @@ export class ACLRuleCollection {
|
||||
this._tableIds = [...tableIds];
|
||||
this._userAttributeRules = userAttributeMap;
|
||||
}
|
||||
|
||||
private _safeReadAclRules(docData: DocData, options: ReadAclOptions): ReadAclResults {
|
||||
try {
|
||||
this.ruleError = undefined;
|
||||
return readAclRules(docData, options);
|
||||
} catch(e) {
|
||||
this.ruleError = e; // Report the error indirectly.
|
||||
return {ruleSets: [EMERGENCY_RULE_SET], userAttributes: []};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface ReadAclOptions {
|
||||
|
||||
@@ -42,6 +42,7 @@ export interface OpenLocalDocResult {
|
||||
doc: {[tableId: string]: TableDataAction};
|
||||
log: ActionGroup[];
|
||||
plugins: LocalPlugin[];
|
||||
recoveryMode?: boolean;
|
||||
}
|
||||
|
||||
export interface DocListAPI {
|
||||
|
||||
@@ -308,6 +308,7 @@ export interface DocAPI {
|
||||
replace(source: DocReplacementOptions): Promise<void>;
|
||||
getSnapshots(): Promise<DocSnapshots>;
|
||||
forceReload(): Promise<void>;
|
||||
recover(recoveryMode: boolean): Promise<void>;
|
||||
// Compare two documents, optionally including details of the changes.
|
||||
compareDoc(remoteDocId: string, options?: { detail: boolean }): Promise<DocStateComparison>;
|
||||
// Compare two versions within a document, including details of the changes.
|
||||
@@ -715,6 +716,13 @@ export class DocAPIImpl extends BaseAPI implements DocAPI {
|
||||
});
|
||||
}
|
||||
|
||||
public async recover(recoveryMode: boolean): Promise<void> {
|
||||
await this.request(`${this._url}/recover`, {
|
||||
body: JSON.stringify({recoveryMode}),
|
||||
method: 'POST'
|
||||
});
|
||||
}
|
||||
|
||||
public async compareDoc(remoteDocId: string, options: {
|
||||
detail?: boolean
|
||||
} = {}): Promise<DocStateComparison> {
|
||||
|
||||
Reference in New Issue
Block a user