(core) allow non-owners to remove themselves from sites/workspaces/docs

Summary:
For users who cannot otherwise change access to a resource, let
them remove themselves. Implemented via the standard endpoints
as a special exception that will process a request from a user
that would otherwise be denied, if the only contents of that
request are a removal of themselves.

Users who can change access are still not permitted to change their
own permissions or to remove themselves, as a precaution against
orphaning resources.

Test Plan: extended and updated tests

Reviewers: cyprien

Reviewed By: cyprien

Subscribers: dsagal

Differential Revision: https://phab.getgrist.com/D3367
This commit is contained in:
Paul Fitzpatrick
2022-04-12 15:31:41 -04:00
parent 25e40bfa9b
commit 20dd2fc70d
10 changed files with 165 additions and 48 deletions

View File

@@ -5,8 +5,8 @@ import {ShareAnnotations, ShareAnnotator} from 'app/common/ShareAnnotator';
import {normalizeEmail} from 'app/common/emails';
import {GristLoadConfig} from 'app/common/gristUrls';
import * as roles from 'app/common/roles';
import {ANONYMOUS_USER_EMAIL, EVERYONE_EMAIL, Organization, PermissionData, PermissionDelta,
UserAPI, Workspace} from 'app/common/UserAPI';
import {ANONYMOUS_USER_EMAIL, Document, EVERYONE_EMAIL, Organization,
PermissionData, PermissionDelta, UserAPI, Workspace} from 'app/common/UserAPI';
import {getRealAccess} from 'app/common/UserAPI';
import {computed, Computed, Disposable, obsArray, ObsArray, observable, Observable} from 'grainjs';
import some = require('lodash/some');
@@ -22,8 +22,10 @@ export interface UserManagerModel {
publicMember: IEditableMember|null; // Member whose access (VIEWER or null) represents that of
// anon@ or everyone@ (depending on the settings and resource).
isAnythingChanged: Computed<boolean>; // Indicates whether there are unsaved changes
isSelfRemoved: Computed<boolean>; // Indicates whether current user is removed
isOrg: boolean; // Indicates if the UserManager is for an org
annotations: Observable<ShareAnnotations>; // More information about shares, keyed by email.
isPersonal: boolean; // If user access info/control is restricted to self.
gristDoc: GristDoc|null; // Populated if there is an open document.
@@ -119,6 +121,8 @@ export class UserManagerModelImpl extends Disposable implements UserManagerModel
public annotations = this.autoDispose(observable({users: new Map()}));
public isPersonal = this.initData.personal || false;
public isOrg: boolean = this.resourceType === 'organization';
public gristDoc: GristDoc|null;
@@ -133,6 +137,11 @@ export class UserManagerModelImpl extends Disposable implements UserManagerModel
(this.publicMember ? isMemberChangedFn(this.publicMember) : false);
}));
// Check if the current user is being removed.
public readonly isSelfRemoved: Computed<boolean> = this.autoDispose(computed<boolean>((use) => {
return some(use(this.membersEdited), m => m.isRemoved && m.email ===this._options.activeEmail);
}));
private _shareAnnotator?: ShareAnnotator;
constructor(