import type { EffectivePermissions, Permission } from '@/lib/auth/permissions' /** * Schreibgrenzen der Freigabestufe, siehe docs/03-auth-und-rechte.md. * * Die Leseprüfung steckt in `visibleCaseWhere`. Diese Regeln kommen obendrauf * und gelten für jede Server Action, unabhängig davon, was die Oberfläche * vorher versteckt hat. */ /** Status, in denen eine Akte nicht mehr verändert wird. */ export const READ_ONLY_STATUS = ['ARCHIVED', 'SEALED'] as const export type WriteDenial = | { ok: false; reason: 'no-permission'; permission: Permission } | { ok: false; reason: 'clearance-too-low'; required: number } | { ok: false; reason: 'read-only'; status: string } | { ok: false; reason: 'closed' } export type WriteCheck = { ok: true } | WriteDenial function requirePermission(p: EffectivePermissions, permission: Permission): WriteCheck { if (p.isAdmin || p.permissions.has(permission)) return { ok: true } return { ok: false, reason: 'no-permission', permission } } /** * Darf dieser Nutzer eine Akte mit dieser Klassifizierung anlegen? * * Niemand legt eine Akte über der eigenen Stufe an - auch kein Admin, denn er * hätte sonst eine Akte erzeugt, deren Sichtbarkeit er nicht überblickt. * Der Admin hat ohnehin Stufe 5. */ export function canCreateCase(p: EffectivePermissions, classification: number): WriteCheck { const permitted = requirePermission(p, 'case.create') if (!permitted.ok) return permitted if (classification > p.clearanceLevel) { return { ok: false, reason: 'clearance-too-low', required: classification } } return { ok: true } } /** * Darf dieser Nutzer diese Akte verändern? * * Setzt voraus, dass die Akte bereits durch `visibleCaseWhere` gelaufen ist: * was man nicht sehen darf, kann man nicht anfassen. */ export function canEditCase( p: EffectivePermissions, target: { status: string; classification: number }, permission: Permission = 'case.edit', ): WriteCheck { if ((READ_ONLY_STATUS as readonly string[]).includes(target.status)) { // Archiviert und versiegelt sind schreibgeschützt, auch für Admins. return { ok: false, reason: 'read-only', status: target.status } } const permitted = requirePermission(p, permission) if (!permitted.ok) return permitted if (target.classification > p.clearanceLevel && !p.isAdmin) { return { ok: false, reason: 'clearance-too-low', required: target.classification } } if (target.status === 'CLOSED' && permission !== 'case.reopen') { // Änderungen an abgeschlossenen Fällen brauchen ein eigenes Recht und // erzeugen zusätzlich einen SYSTEM-Eintrag. const closedAllowed = requirePermission(p, 'case.edit_closed') if (!closedAllowed.ok) return { ok: false, reason: 'closed' } } return { ok: true } } /** * Darf dieser Nutzer die Klassifizierung auf diesen Wert setzen? * * Die neue Stufe darf die eigene nicht übersteigen. Eine Herabstufung ist der * heikelste Vorgang im System: sie macht Inhalte für Leute sichtbar, die sie * vorher nicht sehen durften. Erlaubt ist sie trotzdem - sie wird nur sichtbar * protokolliert, im Verlauf und im Zugriffsprotokoll. */ export function canReclassify( p: EffectivePermissions, target: { status: string; classification: number }, next: number, ): WriteCheck { if (next < 0 || next > 5) return { ok: false, reason: 'clearance-too-low', required: next } if (next > p.clearanceLevel) { return { ok: false, reason: 'clearance-too-low', required: next } } return canEditCase(p, target) } /** Für Server Actions: wirft mit einer Meldung, die in die Oberfläche darf. */ export function assertWritable(check: WriteCheck, messages: Record): void { if (check.ok) return throw new Error(messages[check.reason] ?? messages.default ?? 'Nicht erlaubt') }