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
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die README; an keiner Stelle Bedeutung, nur Zeichensetzung.
101 lines
3.7 KiB
TypeScript
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')
|
|
}
|