Files
AfiS-Aktensystem/web/lib/access/caseWrite.ts
T
Harlekin e0b03ea336
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

101 lines
3.7 KiB
TypeScript

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<string, string>): void {
if (check.ok) return
throw new Error(messages[check.reason] ?? messages.default ?? 'Nicht erlaubt')
}