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
}