@samitouri / QOSami-HFS / commits / 1b9b157f

admin/fs: "propagate" option when "see" permission is set on a folder

Massimo Melina committed May 6, 2023 at 17:17 UTC 1b9b157f80c3624ce414d9405760a96876e464a9
5 files changed +29 -6
admin/src/FileForm.ts
+13 -2
@@ -105,7 +105,18 @@ export default function FileForm({ file, anyMask, defaultPerms, addToBar, urls }
105 placeholder: "Not on disk, this is a virtual folder",
106 },
107 perm('can_read', "Who can see but not download will be asked to login"),
108 - perm('can_see', "If you don't see, you may download with a direct link"),
108 + perm('can_see', "If you don't see, you may still download with a direct link", {
109 + after: isDir && values.can_see != null
110 + && h(BoolField, {
111 + size: 'small',
112 + label: `Propagate permission inside this folder`,
113 + value: values.propagate?.can_see !== false,
114 + onChange(v) {
115 + const o = { ...values.propagate, can_see: v ? undefined : false } // new "propagate" object
116 + setValues({ ...values, propagate: _.every(o, v => v === undefined) ? null : o })
117 + }
118 + })
119 + }),
120 isDir && perm('can_list', "Permission to see content of folders"),
121 isDir && perm('can_delete', hasSource ? '' : "Works only on folders with source"),
122 isDir && perm('can_upload', hasSource ? '' : "Works only on folders with source", { lg: showAccept ? 6 : 12 }),
@@ -125,7 +136,7 @@ export default function FileForm({ file, anyMask, defaultPerms, addToBar, urls }
136 ]
137 })
138
128 - function perm(perm: keyof typeof inheritedPerms, helperText='', props: Partial<WhoFieldProps>={}) {
139 + function perm(perm: keyof typeof inheritedPerms, helperText?: ReactNode, props: Partial<WhoFieldProps>={}) {
140 return {
141 showInherited: anyMask, // with masks, you may need to set a permission to override the mask
142 otherPerms: _.without(Object.keys(defaultPerms), perm).map(x => ({ value: x, label: "As " +perm2word(x) })),
admin/src/VfsPage.ts
+2
@@ -22,6 +22,7 @@ import FileForm from './FileForm'
22 import { Close, Delete } from '@mui/icons-material'
23 import { alertDialog, confirmDialog } from './dialog'
24 import { Flex } from './misc'
25 +import { VfsPerm } from '../../src/vfs'
26
27 let selectOnReload: string[] | undefined
28
@@ -175,6 +176,7 @@ export interface VfsNode extends VfsPerms {
176 default?: string
177 children?: VfsNode[]
178 parent?: VfsNode
179 + propagate?: Partial<Record<keyof VfsPerm, boolean>> | null
180 website?: true
181 masks?: any
182 byMasks?: VfsPerms
config.md
+8 -2
@@ -63,8 +63,14 @@ Valid keys in a node are:
63 - `[ frank, peter ]`: the list of accounts who can.
64 - `can_see`: specify who can see this element. Even if a user can download you can still make the file not appear in the list.
65 Value is a `WhoCan` descriptor, refer above.
66 -- `can_upload` specify who can upload. Applies to folders with a source. Default is none.
67 -- `can_delete` specify who can delete. Applies to folders with a source. Default is none.
66 +- `can_upload`: specify who can upload. Applies to folders with a source. Default is none.
67 +- `can_delete`: specify who can delete. Applies to folders with a source. Default is none.
68 +- `propagate`: by default, permissions propagate. Use this to stop propagation of some permissions assigned to this node.
69 + For each permission you don't want to propagate you specify the name and set it to false. E.g.
70 + ```
71 + can_see: false
72 + ```
73 + Default is "all propagates".
74 - `masks`: maps a file mask to a set of properties as the one documented in this section. E.g.
75 ```
76 myfile.txt:
src/api.vfs.ts
+1 -1
@@ -104,7 +104,7 @@ const apis: ApiHandlers = {
104 const n = await urlToNodeOriginal(uri)
105 if (!n)
106 return new ApiError(HTTP_NOT_FOUND, 'path not found')
107 - props = pickProps(props, ['name','source','masks','default','accept', ...PERM_KEYS]) // sanitize
107 + props = pickProps(props, ['name','source','masks','default','accept','propagate', ...PERM_KEYS]) // sanitize
108 if (props.name && props.name !== getNodeName(n)) {
109 const parent = await urlToNodeOriginal(dirname(uri))
110 if (parent?.children?.find(x => getNodeName(x) === props.name))
src/vfs.ts
+5 -1
@@ -39,6 +39,7 @@ export interface VfsNode extends Partial<VfsPerm> {
39 rename?: Record<string, string>
40 masks?: Masks // express fields for descendants that are not in the tree
41 accept?: string
42 + propagate?: Record<keyof VfsPerm, boolean>
43 // fields that are only filled at run-time
44 isTemp?: true // this node doesn't belong to the tree and was created by necessity
45 original?: VfsNode // if this is a temp node but reflecting an existing node
@@ -59,7 +60,10 @@ export const MIME_AUTO = 'auto'
60
61 function inheritFromParent(parent: VfsNode, child: VfsNode) {
62 for (const k of typedKeys(defaultPerms)) {
62 - const v = parent[k]
63 + let dueParent: VfsNode | undefined = parent
64 + while (dueParent?.propagate?.[k] === false)
65 + dueParent = dueParent.parent
66 + const v = dueParent?.[k]
67 if (v !== undefined) // small optimization: don't expand the object
68 child[k] ??= v
69 }