@samitouri / QOSami-HFS / commits / 7e5ebdb6

fix: (regression beta) admin/accounts: any new account would store unwanted property "directMembers"

Massimo Melina committed Apr 15, 2025 at 14:59 UTC 7e5ebdb6f8bf42de1a4beec14a2abe7c1879be17
2 files changed +15 -8
src/api.accounts.ts
+6 -1
@@ -9,6 +9,7 @@ import _ from 'lodash'
9 import { HTTP_BAD_REQUEST, HTTP_CONFLICT, HTTP_NOT_FOUND } from './const'
10 import { getCurrentUsername, invalidateSessionBefore } from './auth'
11 import { apiAssertTypes, objFromKeys, onlyTruthy, with_ } from './misc'
12 +import { pickProps } from './api.vfs'
13
14 function prepareAccount(ac: Account | undefined) {
15 return ac && {
@@ -32,6 +33,9 @@ function prepareAccount(ac: Account | undefined) {
33 }
34 }
35
36 +const ALLOWED_KEYS: (keyof Account)[] = ['admin', 'allow_net', 'belongs', 'days_to_live', 'disable_password_change',
37 + 'disabled', 'expire', 'ignore_limits', 'notes', 'password', 'redirect', 'require_password_change', 'username']
38 +
39 export default {
40
41 get_usernames() {
@@ -56,7 +60,7 @@ export default {
60 const acc = getAccount(username)
61 if (!acc)
62 return new ApiError(HTTP_BAD_REQUEST)
59 - await updateAccount(acc, changes)
63 + await updateAccount(acc, pickProps(changes, ALLOWED_KEYS))
64 if (changes.username && ctx.session?.username === username)
65 ctx.session!.username = changes.username
66 return _.pick(acc, 'username')
@@ -65,6 +69,7 @@ export default {
69 async add_account({ overwrite, username, ...rest }) {
70 apiAssertTypes({ string: { username } })
71 const existing = getAccount(username)
72 + rest = pickProps(rest, ALLOWED_KEYS)
73 if (existing) {
74 if (!overwrite) return new ApiError(HTTP_CONFLICT)
75 await updateAccount(existing, rest)
src/api.vfs.ts
+9 -7
@@ -1,7 +1,9 @@
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 -import { getNodeName, isSameFilenameAs, nodeIsDirectory, saveVfs, urlToNode, vfs, VfsNode, applyParentToChild,
4 - permsFromParent, nodeIsLink } from './vfs'
3 +import {
4 + getNodeName, isSameFilenameAs, nodeIsDirectory, saveVfs, urlToNode, vfs, VfsNode, applyParentToChild,
5 + permsFromParent, nodeIsLink, VfsNodeStored
6 +} from './vfs'
7 import _ from 'lodash'
8 import { mkdir, stat } from 'fs/promises'
9 import { ApiError, ApiHandlers } from './apiMiddleware'
@@ -25,7 +27,8 @@ async function urlToNodeOriginal(uri: string) {
27 return n?.isTemp ? n.original : n
28 }
29
28 -const ALLOWED_KEYS = ['name','source','masks','default','accept','rename','mime','url','target','comment','icon','order', ...PERM_KEYS]
30 +const ALLOWED_KEYS: (keyof VfsNodeStored)[] = ['name', 'source', 'masks', 'default', 'accept', 'rename', 'mime', 'url',
31 + 'target', 'comment', 'icon', 'order', ...PERM_KEYS]
32
33 export interface LsEntry { n:string, s?:number, m?:string, c?:string, k?:'d' }
34
@@ -92,7 +95,6 @@ const apis: ApiHandlers = {
95 const n = await urlToNodeOriginal(uri)
96 if (!n)
97 return new ApiError(HTTP_NOT_FOUND, 'path not found')
95 - props = pickProps(props, ALLOWED_KEYS) // sanitize
98 if (props.name && props.name !== getNodeName(n)) {
99 if (!isValidFileName(props.name))
100 return new ApiError(HTTP_BAD_REQUEST, 'bad name')
@@ -102,7 +104,7 @@ const apis: ApiHandlers = {
104 }
105 if (props.masks && typeof props.masks !== 'object')
106 delete props.masks
105 - Object.assign(n, props)
107 + Object.assign(n, pickProps(props, ALLOWED_KEYS))
108 simplifyName(n)
109 saveVfs()
110 return n
@@ -268,8 +270,8 @@ const apis: ApiHandlers = {
270
271 export default apis
272
271 -// pick only selected props, and consider null and empty string as undefined
272 -function pickProps(o: any, keys: string[]) {
273 +// pick only selected props, and consider null and empty string as undefined, as it's the default value and we don't want to store it
274 +export function pickProps(o: any, keys: string[]) {
275 const ret: any = {}
276 if (o && typeof o === 'object')
277 for (const k of keys)