diff options
Diffstat (limited to 'src/fields')
-rw-r--r-- | src/fields/Doc.ts | 67 | ||||
-rw-r--r-- | src/fields/util.ts | 60 |
2 files changed, 98 insertions, 29 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index bbc9d388a..c965dc282 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -17,8 +17,9 @@ import { RichTextField } from "./RichTextField"; import { listSpec } from "./Schema"; import { ComputedField } from "./ScriptField"; import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; -import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util"; +import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction, getEffectiveAcl } from "./util"; import { LinkManager } from "../client/util/LinkManager"; +import { SharingPermissions } from "../client/util/SharingManager"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -100,23 +101,39 @@ export const AclEdit = Symbol("AclEdit"); export const UpdatingFromServer = Symbol("UpdatingFromServer"); const CachedUpdates = Symbol("Cached updates"); +const AclMap = new Map<string, symbol>([ + [SharingPermissions.None, AclPrivate], + [SharingPermissions.View, AclReadonly], + [SharingPermissions.Add, AclAddonly], + [SharingPermissions.Edit, AclEdit] +]); export function fetchProto(doc: Doc) { + console.log("in fetchproto"); if (doc.author !== Doc.CurrentUserEmail) { // storing acls for groups needs to be extended here - AclSym should store a datastructure that stores information about permissions - const acl = Doc.Get(doc, "ACL", true); - switch (acl) { - case "ownerOnly": - doc[AclSym] = AclPrivate; - return undefined; - case "readOnly": - doc[AclSym] = AclReadonly; - break; - case "addOnly": - doc[AclSym] = AclAddonly; - break; - case "edit": - doc[AclSym] = AclEdit; - } + + const permissions: { [key: string]: symbol } = {}; + + Object.keys(doc).forEach(key => { + if (key.startsWith("ACL")) permissions[key] = AclMap.get(StrCast(doc[key]))!; + }); + + doc[AclSym] = permissions; + + // const acl = Doc.Get(doc, "ACL", true); + // switch (acl) { + // case "ownerOnly": + // doc[AclSym] = AclPrivate; + // return undefined; + // case "readOnly": + // doc[AclSym] = AclReadonly; + // break; + // case "addOnly": + // doc[AclSym] = AclAddonly; + // break; + // // case "edit": + // // doc[AclSym] = AclEdit; + // } } if (doc.proto instanceof Promise) { @@ -134,10 +151,10 @@ export class Doc extends RefField { set: setter, get: getter, // getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter - has: (target, key) => target[AclSym] !== AclPrivate && key in target.__fields, + has: (target, key) => getEffectiveAcl(target) !== AclPrivate && key in target.__fields, ownKeys: target => { const obj = {} as any; - if (target[AclSym] !== AclPrivate) Object.assign(obj, target.___fields); + if (getEffectiveAcl(target) !== AclPrivate) Object.assign(obj, target.___fields); runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__); return Object.keys(obj); }, @@ -191,11 +208,11 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [AclSym]: any = undefined; + public [AclSym]: any; public [WidthSym] = () => NumCast(this[SelfProxy]._width); public [HeightSym] = () => NumCast(this[SelfProxy]._height); public [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; } - public [ToString]() { return `Doc(${this[AclSym] === AclPrivate ? "-inaccessible-" : this.title})`; } + public [ToString]() { return `Doc(${getEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; } public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; } public get [DataSym]() { const self = this[SelfProxy]; @@ -823,7 +840,8 @@ export namespace Doc { } // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message) export function IsBrushedDegreeUnmemoized(doc: Doc) { - if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return 0; + console.log("here"); + if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return 0; return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0; } export function IsBrushedDegree(doc: Doc) { @@ -832,15 +850,15 @@ export namespace Doc { })(doc); } export function BrushDoc(doc: Doc) { - - if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return doc; + console.log("here"); + if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; brushManager.BrushedDoc.set(doc, true); brushManager.BrushedDoc.set(Doc.GetProto(doc), true); return doc; } export function UnBrushDoc(doc: Doc) { - if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return doc; + if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; brushManager.BrushedDoc.delete(doc); brushManager.BrushedDoc.delete(Doc.GetProto(doc)); return doc; @@ -870,7 +888,8 @@ export namespace Doc { } const highlightManager = new HighlightBrush(); export function IsHighlighted(doc: Doc) { - if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return false; + console.log("here"); + if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return false; return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetProto(doc)); } export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true) { diff --git a/src/fields/util.ts b/src/fields/util.ts index 7bb090a93..a3e7a36f8 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,5 +1,5 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym, AclSym, AclPrivate } from "./Doc"; +import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym, AclSym, AclPrivate, AclEdit, AclReadonly, AclAddonly } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -9,6 +9,7 @@ import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols"; import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; import { ScriptCast } from "./Types"; +import GroupManager from "../client/util/GroupManager"; function _readOnlySetter(): never { @@ -107,11 +108,57 @@ export function OVERRIDE_ACL(val: boolean) { _overrideAcl = val; } +const HierarchyMapping = new Map<symbol, number>([ + [AclPrivate, 0], + [AclReadonly, 1], + [AclAddonly, 2], + [AclEdit, 3] +]); + +export function getEffectiveAcl(target: any): symbol { + + console.log("in getEffectiveAcl"); + + if (target[AclSym].ACL) return target[AclSym].ACL; + + let effectiveAcl = AclEdit; + + for (const [key, value] of Object.entries(target[AclSym])) { + if (key.startsWith("ACL-")) { + if (GroupManager.Instance.currentUserGroups.includes(key.substring(4)) || Doc.CurrentUserEmail === key.substring(4).replace("_", ".")) { + if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) { + effectiveAcl = value as symbol; + if (effectiveAcl === AclEdit) break; + } + } + } + } + + return effectiveAcl; +} + +function testPermission(target: any, in_prop: string | symbol | number): boolean { + + console.log("here"); + // if (target[AclSym].ACL !== AclEdit && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return false; + if (target[AclSym].ACL === AclEdit) return true; + for (const [key, value] of Object.entries(target[AclSym])) { + if (key.startsWith("ACL-")) { + if (GroupManager.Instance.currentUserGroups.includes(key.substring(4))) { + if (value === AclEdit) return true; + } + } + } + return _overrideAcl || DocServer.PlaygroundFields.includes(in_prop.toString()); +} + const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { + console.log("in setter") let prop = in_prop; - if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true; // generalise to a testpermission function + // if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true; // generalise to a testpermission function + if (!testPermission(target, in_prop)) return true; if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { if (!prop.startsWith("_")) { console.log(prop + " is deprecated - switch to _" + prop); @@ -129,9 +176,11 @@ export function setter(target: any, in_prop: string | symbol | number, value: an } export function getter(target: any, in_prop: string | symbol | number, receiver: any): any { + console.log("in getter") let prop = in_prop; - if (in_prop === AclSym) return _overrideAcl ? undefined : target[AclSym]; - if (target[AclSym] === AclPrivate && !_overrideAcl) return undefined; + const effectiveAcl = getEffectiveAcl(target); + if (in_prop === AclSym) return _overrideAcl ? undefined : effectiveAcl; + if (effectiveAcl === AclPrivate && !_overrideAcl) return undefined; if (prop === LayoutSym) { return target.__LAYOUT__; } @@ -168,7 +217,8 @@ function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreP } if (field === undefined && !ignoreProto && prop !== "proto") { const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters - if (proto instanceof Doc && proto[AclSym] !== AclPrivate) { + console.log("here"); + if (proto instanceof Doc && getEffectiveAcl(proto) !== AclPrivate) { return getFieldImpl(proto[Self], prop, receiver, ignoreProto); } return undefined; |