small optimization on permission checks

Massimo Melina committed Oct 24, 2025 at 19:38 UTC 7052a6bb37a6b7e6be446f0ae03856598d0fb03e
2 files changed +7 -7
src/api.auth.ts
+1 -1
@@ -111,7 +111,7 @@ export const refresh_session: ApiHandler = async ({}, ctx) => {
111 const username = getCurrentUsername(ctx)
112 return !ctx.session ? new ApiError(HTTP_SERVER_ERROR) : {
113 username,
114 - expandedUsername: expandUsername(username),
114 + expandedUsername: Array.from(expandUsername(username)),
115 adminUrl: ctxAdminAccess(ctx) ? ctx.state.revProxyPath + ADMIN_URI : undefined,
116 canChangePassword: accountCanChangePassword(ctx.state.account),
117 requireChangePassword: ctx.state.account?.require_password_change,
src/perm.ts
+6 -6
@@ -30,13 +30,13 @@ export interface Account {
30 interface Accounts { [username:string]: Account }
31
32 // provides the username and all other usernames it inherits based on the 'belongs' attribute. Useful to check permissions
33 -export function expandUsername(who: string): string[] {
34 - const ret = []
33 +export function expandUsername(who: string) {
34 + const ret = new Set<string>()
35 const q = [who]
36 for (const u of q) {
37 const a = getAccount(u)
38 if (!a || a.disabled) continue
39 - ret.push(u)
39 + ret.add(u)
40 if (a.belongs)
41 q.push(...a.belongs)
42 }
@@ -45,8 +45,8 @@ export function expandUsername(who: string): string[] {
45
46 // check if current username or any ancestor match the provided usernames
47 export function ctxBelongsTo(ctx: Koa.Context, usernames: string[]) {
48 - return (ctx.state.usernames ||= expandUsername(getCurrentUsername(ctx))) // cache ancestors' usernames inside context state
49 - .some((u: string) => usernames.includes(u))
48 + const s = ctx.state.usernames ||= expandUsername(getCurrentUsername(ctx))
49 + return usernames.some(u => s.has(u)) // cache ancestors' usernames inside context state
50 }
51
52 export function getUsernames() {
@@ -227,6 +227,6 @@ export async function changeSrpHelper(account: Account, salt: string, verifier:
227
228 declare module "koa" {
229 interface DefaultState {
230 - usernames?: string[]
230 + usernames?: Set<string>
231 }
232 }
\ No newline at end of file