@samitouri / QOSami-HFS / commits / 2355754e

maskOnly

Massimo Melina committed May 27, 2023 at 23:37 UTC 2355754e93f65d177e67d579369dbb5eff83c7f2
2 files changed +20 -13
config.md
+2
@@ -82,6 +82,8 @@ Valid keys in a node are:
82 mime: auto
83 ```
84 Rules on top have priority over bottom rules. Inner rules have priority over parent's rules.
85 + A mask can carry any node property (even property "masks") plus a special property `maskOnly` (optional) to restrict
86 + the application of the mask to only files or folders, just by specifying exactly `files` or `folders`
87
88 Permissions set on an inner element will override inherited permissions. This means that you can restrict access to folder1,
89 and yet decide to give free access to folder1/subfolder2.
src/vfs.ts
+18 -13
@@ -2,7 +2,17 @@
2
3 import fs from 'fs/promises'
4 import { basename, dirname, join, resolve } from 'path'
5 -import { dirStream, dirTraversal, enforceFinal, getOrSet, isDirectory, typedKeys, makeMatcher, setHidden } from './misc'
5 +import {
6 + dirStream,
7 + dirTraversal,
8 + enforceFinal,
9 + getOrSet,
10 + isDirectory,
11 + typedKeys,
12 + makeMatcher,
13 + setHidden,
14 + onlyTruthy
15 +} from './misc'
16 import Koa from 'koa'
17 import _ from 'lodash'
18 import { defineConfig, setConfig } from './config'
@@ -28,7 +38,7 @@ export interface VfsPerm {
38 can_delete: Who
39 }
40
31 -type Masks = Record<string, VfsNode>
41 +type Masks = Record<string, VfsNode & { maskOnly?: 'files' | 'folders' }>
42
43 export interface VfsNode extends Partial<VfsPerm> {
44 name?: string
@@ -296,18 +306,13 @@ export function masksCouldGivePermission(masks: Masks | undefined, perm: keyof V
306 }
307
308 export function parentMaskApplier(parent: VfsNode) {
299 - const matchers = Object.entries(parent.masks || {}).map(([k, v]) => {
309 + const matchers = onlyTruthy(Object.entries(parent.masks || {}).map(([k, { maskOnly, ...mods }]) => {
310 k = k.startsWith('**/') ? k.slice(3) : !k.includes('/') ? k : ''
301 - if (!k) return
302 - const m = makeMatcher(k)
303 - return [m, v] as [typeof m, typeof v]
304 - })
305 - return (item: VfsNode, virtualBasename?: string) => {
306 - if (virtualBasename === undefined)
307 - virtualBasename = getNodeName(item)
308 - for (const entry of matchers) {
309 - if (!entry) continue
310 - const [matcher, mods] = entry
311 + return k && { mods, maskOnly, matcher: makeMatcher(k) }
312 + }))
313 + return (item: VfsNode, virtualBasename=getNodeName(item)) => {
314 + for (const { matcher, mods, maskOnly } of matchers) {
315 + if (maskOnly === 'folders' && !item.isFolder || maskOnly === 'files' && item.isFolder) continue
316 if (!matcher(virtualBasename)) continue
317 if (item.masks)
318 item.masks = _.merge(_.cloneDeep(mods.masks), item.masks) // item.masks must take precedence