harden api updateAccount and set_account
Massimo Melina committed
Oct 28, 2025 at 10:48 UTC
673bb63b7dd049c4ea51e509e6894709f0b47bc0
2 files changed
+9
-5
src/api.accounts.ts
+2
-2
@@ -62,7 +62,7 @@ export default {
62
if (!acc)
63
return new ApiError(HTTP_BAD_REQUEST)
64
await updateAccount(acc, pickProps(changes, ALLOWED_KEYS))
65
- if (changes.username && ctx.session?.username === normalizeUsername(username))
65
+ if (changes.username && ctx.session?.username === normalizeUsername(username)) // update session if necessary
66
ctx.session!.username = normalizeUsername(changes.username)
67
return _.pick(acc, 'username')
68
},
@@ -91,7 +91,7 @@ export default {
91
92
invalidate_sessions({ username }) {
93
apiAssertTypes({ string: { username } })
94
- invalidateSessionBefore.set(username, Date.now())
94
+ invalidateSessionBefore.set(normalizeUsername(username), Date.now())
95
return {}
96
},
97
src/perm.ts
+7
-3
@@ -9,9 +9,9 @@ import { ApiError } from './apiMiddleware'
9
import { getCurrentUsername } from './auth'
10
import Koa from 'koa'
11
12
+// for all the Account fields, falsy values must be equivalent to undefined. If this changes in the future, please adjust addAccount and setAccount
13
export interface Account {
13
- // we consider all the following fields, when falsy, as equivalent to be missing. If this changes in the future, please adjust addAccount and setAccount
14
- username: string, // we keep username property (hidden) so we don't need to pass it separately
14
+ username: string, // we keep username property for convenience, but hidden as we don't persist it inside the object, but as key of the accounts map
15
password?: string
16
srp?: string
17
belongs?: string[]
@@ -84,8 +84,12 @@ export async function updateAccount(account: Account, change: Partial<Account> |
84
const { username: usernameWas } = account
85
if (typeof change === 'function')
86
await change?.(account)
87
- else
87
+ else {
88
+ const u = normalizeUsername(change.username || '')
89
+ if (u && u !== usernameWas && getAccount(u))
90
+ throw "username already exists"
91
Object.assign(account, objSameKeys(change, x => x || undefined))
92
+ }
93
for (const [k,v] of typedEntries(account))
94
if (!v) delete account[k] // we consider all account fields, when falsy, as equivalent to be missing (so, default value applies)
95
const { username, password } = account