diff options
Diffstat (limited to 'src/fields/util.ts')
-rw-r--r-- | src/fields/util.ts | 125 |
1 files changed, 37 insertions, 88 deletions
diff --git a/src/fields/util.ts b/src/fields/util.ts index 068323dce..0e9940ced 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -14,6 +14,7 @@ import { List } from './List'; import { ObjectField } from './ObjectField'; import { PrefetchProxy, ProxyField } from './Proxy'; import { RefField } from './RefField'; +import { RichTextField } from './RichTextField'; import { SchemaHeaderField } from './SchemaHeaderField'; import { ComputedField } from './ScriptField'; import { ScriptCast, StrCast } from './Types'; @@ -62,8 +63,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number const writeMode = DocServer.getFieldWriteMode(prop as string); const fromServer = target[UpdatingFromServer]; const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail; - const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly; - const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin) && !DocServer.Control.isReadOnly(); + const writeToDoc = + sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Playground || writeMode === DocServer.WriteMode.LivePlayground || (effectiveAcl === AclAugment && value instanceof RichTextField); + const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclAugment && Doc.CurrentUserEmail !== 'guest' && value instanceof RichTextField)) && !DocServer.Control.isReadOnly(); if (writeToDoc) { if (value === undefined) { @@ -104,7 +106,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number ); return true; } - return false; + return true; }); let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl; @@ -128,13 +130,14 @@ export function denormalizeEmail(email: string) { * Copies parent's acl fields to the child */ export function inheritParentAcls(parent: Doc, child: Doc) { - for (const key of Object.keys(Doc.GetProto(parent))) { + if (GetEffectiveAcl(parent) !== AclAdmin) return; + for (const key of Object.keys(parent)) { // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private. - // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : Doc.GetProto(parent)[key]; - const symbol = ReverseHierarchyMap.get(StrCast(Doc.GetProto(parent)[key])); + // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : parent[key]; + const symbol = ReverseHierarchyMap.get(StrCast(parent[key])); if (symbol) { const sharePermission = HierarchyMapping.get(symbol.acl!)!.name; - key.startsWith('acl') && distributeAcls(key, sharePermission, Doc.GetProto(child)); + key.startsWith('acl') && distributeAcls(key, sharePermission, child); } } } @@ -168,30 +171,16 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) { return getEffectiveAcl(target, user); }, true); -// return layout acl from cache or chache the acl and return. -const getEffectiveLayoutAclCache = computedFn(function (target: any, user?: string) { - return getEffectiveLayoutAcl(target, user); -}, true); - /** * Calculates the effective access right to a document for the current user. */ export function GetEffectiveAcl(target: any, user?: string): symbol { if (!target) return AclPrivate; if (target[UpdatingFromServer]) return AclAdmin; - return getEffectiveAclCache(Doc.GetProto(target), user); // all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable) -} - -/** - * Calculates the effective access layout right to a document for the current user. By getting the container's effective acl if the layout acl isn't set. - */ -export function GetEffectiveLayoutAcl(target: any, user?: string): symbol { - if (!target) return AclPrivate; - if (target[UpdatingFromServer]) return AclAdmin; - return getEffectiveLayoutAclCache(target, user); + return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable) } -function getPropAcl(target: any, prop: string | symbol | number) { +export function GetPropAcl(target: any, prop: string | symbol | number) { if (typeof prop === 'symbol' || target[UpdatingFromServer]) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable return GetEffectiveAcl(target); @@ -209,13 +198,9 @@ export function SetCachedGroups(groups: string[]) { } function getEffectiveAcl(target: any, user?: string): symbol { const targetAcls = target[DocAcl]; - // if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin; + if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin; const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group - - // guest dashboard permissions error - if (userChecked == 'guest' && target._type_collection == CollectionViewType.Docking) return AclAugment; - if (targetAcls && Object.keys(targetAcls).length) { let effectiveAcl = AclPrivate; for (const [key, value] of Object.entries(targetAcls)) { @@ -238,37 +223,6 @@ function getEffectiveAcl(target: any, user?: string): symbol { } /** - * Returns the layout acl that is effective on the document passed through as the target. If no layout acls - * have been set, it returns the regular acls for the document target is contained in. - */ -function getEffectiveLayoutAcl(target: any, user?: string): symbol { - const targetAcls = target[DocAcl]; - - const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group - if (targetAcls && Object.keys(targetAcls).length) { - var effectiveAcl; - for (const [key, value] of Object.entries(targetAcls)) { - const entity = denormalizeEmail(key.substring(4)); // an individual or a group - if ((GetCachedGroupByName(entity) || userChecked === entity || entity === 'Me') && entity != 'Public') { - if (effectiveAcl && HierarchyMapping.get(value as symbol)!.level > HierarchyMapping.get(effectiveAcl)!.level) { - effectiveAcl = value as symbol; - } else { - effectiveAcl = value as symbol; - } - } - } - - if (effectiveAcl) { - return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl; - } - } - // authored documents are private until an ACL is set. - const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc or Proxy, so check __fieldTuples.author and .author - if (targetAuthor && targetAuthor !== userChecked) return AclPrivate; - return AclAdmin; -} - -/** * Recursively distributes the access right for a user across the children of a document and its annotations. * @param key the key storing the access right (e.g. acl-groupname) * @param acl the access right being stored (e.g. "Can Edit") @@ -276,56 +230,51 @@ function getEffectiveLayoutAcl(target: any, user?: string): symbol { * @param inheritingFromCollection whether the target is being assigned rights after being dragged into a collection (and so is inheriting the acls from the collection) * inheritingFromCollection is not currently being used but could be used if acl assignment defaults change */ -export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[], isDashboard?: boolean) { +export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[]) { if (!visited) visited = [] as Doc[]; if (!target || visited.includes(target)) return; if ((target._type_collection === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) { - target[key] = acl; - // if (target !== Doc.GetProto(target)) { - // //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???) - // updateCachedAcls(target); - // } + if (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`) { + target[key] = acl; + } + if (target !== Doc.GetProto(target)) { + //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???) + updateCachedAcls(target); + } //return; } visited.push(target); let layoutDocChanged = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym]) // if it is inheriting from a collection, it only inherits if A) the key doesn't already exist or B) the right being inherited is more restrictive - // taken care of in SharingManager now so that users can decide if they want nested docs changed - // if (GetEffectiveAcl(target) === AclAdmin && (!inheritingFromCollection || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) { - // target[key] = acl; - // layoutDocChanged = true; - // if (isDashboard) { - // DocListCastAsync(target[Doc.LayoutFieldKey(target)]).then(docs => { - // docs?.forEach(d => distributeAcls(key, acl, d, inheritingFromCollection, visited)); - // }); - // } - // } + if (GetEffectiveAcl(target) === AclAdmin && (!inheritingFromCollection || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) { + if (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`) { + target[key] = acl; + layoutDocChanged = true; + } + } let dataDocChanged = false; const dataDoc = target[DocData]; if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || ReverseHierarchyMap.get(StrCast(dataDoc[key]))! > ReverseHierarchyMap.get(acl)!)) { - - // changing doc proto - but this messes up layout - // if (GetEffectiveAcl(dataDoc) === AclAdmin) { - // dataDoc[key] = acl; - // dataDocChanged = true; - // } + if (GetEffectiveAcl(dataDoc) === AclAdmin && (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`)) { + dataDoc[key] = acl; + dataDocChanged = true; + } // maps over the links of the document LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited)); // maps over the children of the document - // taken care of in SharingManager now so that users can decide if they want nested docs changed - // DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => { - // distributeAcls(key, acl, d, inheritingFromCollection, visited); - // distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited); - // }); + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => { + distributeAcls(key, acl, d, inheritingFromCollection, visited); + d !== d[DocData] && distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited); + }); // maps over the annotations of the document DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => { distributeAcls(key, acl, d, inheritingFromCollection, visited); - distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited); + d !== d[DocData] && distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited); }); } @@ -335,7 +284,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { let prop = in_prop; - const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : getPropAcl(target, prop); + const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : GetPropAcl(target, prop); if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin) return true; // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't if (typeof prop === 'string' && prop.startsWith('acl') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true; |