Files
familienarchiv/frontend/src/lib/shared/server/permissions.ts
Marcel 719274ef88
Some checks failed
CI / Semgrep Security Scan (pull_request) Successful in 25s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m6s
SDD Gate / RTM Check (pull_request) Successful in 14s
SDD Gate / Contract Validate (pull_request) Successful in 23s
SDD Gate / Constitution Impact (pull_request) Successful in 21s
CI / Unit & Component Tests (pull_request) Failing after 3m19s
CI / OCR Service Tests (pull_request) Successful in 22s
CI / Backend Unit Tests (pull_request) Successful in 4m49s
CI / fail2ban Regex (pull_request) Successful in 47s
docs(permissions): note requireWriteAll can replace the inline guard elsewhere
Architect/Developer review suggestion: flag that other WRITE_ALL-gated author
loads (e.g. documents/[id]/edit) still inline the throw-403 guard and can adopt
requireWriteAll so it doesn't diverge. Comment-only.

Addresses PR #832 review (Architect suggestion).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 00:33:38 +02:00

31 lines
1.3 KiB
TypeScript

import { error } from '@sveltejs/kit';
/**
* Server-side permission predicates derived from the authenticated user in `locals`.
*
* The user shape is intentionally narrowed to the only field these checks read
* (`groups[].permissions`) so the helper works against `App.Locals` without importing it.
*/
type PermissionLocals = {
user?: { groups?: { permissions: string[] }[] } | null;
};
/** True when any of the user's groups grants WRITE_ALL. False for anonymous users. */
export function hasWriteAll(locals: PermissionLocals): boolean {
return locals.user?.groups?.some((group) => group.permissions.includes('WRITE_ALL')) ?? false;
}
/**
* Throws a 403 unless the user holds WRITE_ALL. Anonymous users are rejected too
* — `hasWriteAll` returns false for a null user, so a single check covers both
* the unauthenticated and the under-privileged case. Server-side gate; the
* frontend canWrite flag only hides entry-point buttons.
*
* Other WRITE_ALL-gated author loads (e.g. `documents/[id]/edit`) still inline
* `if (!hasWriteAll(locals)) throw error(403)` — they can adopt this helper so
* the guard doesn't quietly diverge across routes.
*/
export function requireWriteAll(locals: PermissionLocals): void {
if (!hasWriteAll(locals)) throw error(403, 'Forbidden');
}