feat(auth): Anmeldung, Rollen und Benutzerverwaltung (#21)
OIDC mit Discovery, PKCE, state und nonce; lokaler Login mit argon2 als Notfallzugang; Rechte in einer Tabelle, Prüfung ausschließlich serverseitig, Rolle und Aktiv-Status pro Anfrage aus der Datenbank. Ein unabhängiger Review fand eine offene Weiterleitung: die Präfix-Prüfung des Rücksprungziels ließ /<TAB>/fremd.example durch, woraus der Browser //fremd.example macht. Das Ziel wird jetzt aufgelöst und die Herkunft verglichen. Dazu acht weitere Befunde, alle behoben. 274 Tests. Closes #7 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit was merged in pull request #21.
This commit is contained in:
+28
-3
@@ -5,6 +5,8 @@
|
||||
* die Huelle von Anfang an die echte Struktur, ohne auf Seiten zu verlinken, die
|
||||
* es noch nicht gibt. Die Beschriftungen haengen am Betriebsmodus (#008).
|
||||
*/
|
||||
import type { Recht } from "@/server/auth/rollen";
|
||||
|
||||
export type Betriebsmodus = "INTERN" | "DIENSTLEISTUNG";
|
||||
|
||||
export type NavEintrag = {
|
||||
@@ -15,6 +17,8 @@ export type NavEintrag = {
|
||||
/** Nur in diesem Modus zeigen. Fehlt der Wert, gilt der Eintrag in beiden. */
|
||||
readonly nurModus?: Betriebsmodus;
|
||||
readonly symbol: string;
|
||||
/** Wer dieses Recht nicht hat, sieht den Eintrag gar nicht erst. */
|
||||
readonly recht?: Recht;
|
||||
readonly verfuegbar: boolean;
|
||||
/** Issue, das die Seite liefert – nur fuer den Hinweis, solange sie fehlt. */
|
||||
readonly ausIssue?: number;
|
||||
@@ -41,11 +45,32 @@ export const NAVIGATION: readonly NavEintrag[] = [
|
||||
ausIssue: 8,
|
||||
},
|
||||
{ pfad: "/archiv", beschriftung: "Archiv", symbol: "Archive", verfuegbar: false, ausIssue: 15 },
|
||||
{ pfad: "/admin", beschriftung: "Admin", symbol: "Settings", verfuegbar: false, ausIssue: 8 },
|
||||
{
|
||||
// Zeigt vorerst direkt auf die Benutzerverwaltung; mit #008 kommen die
|
||||
// Einstellungen dazu und daraus wird ein eigener Bereich.
|
||||
pfad: "/admin/benutzer",
|
||||
beschriftung: "Admin",
|
||||
symbol: "Settings",
|
||||
recht: "verwaltung",
|
||||
verfuegbar: true,
|
||||
},
|
||||
];
|
||||
|
||||
export function navigationFuer(modus: Betriebsmodus): readonly NavEintrag[] {
|
||||
return NAVIGATION.filter((e) => e.nurModus === undefined || e.nurModus === modus).map((e) =>
|
||||
/**
|
||||
* Die sichtbaren Eintraege fuer diesen Modus und diese Rolle.
|
||||
*
|
||||
* Das Ausblenden ist Bequemlichkeit, keine Absicherung – die Rechtepruefung
|
||||
* sitzt in der Service-Schicht. Aber ein Leser soll nicht auf Knoepfe klicken,
|
||||
* die ihm hinterher eine Fehlermeldung zeigen.
|
||||
*/
|
||||
export function navigationFuer(
|
||||
modus: Betriebsmodus,
|
||||
darfEintrag: (recht: Recht) => boolean = () => true,
|
||||
): readonly NavEintrag[] {
|
||||
return NAVIGATION.filter(
|
||||
(e) =>
|
||||
(e.nurModus === undefined || e.nurModus === modus) && (e.recht === undefined || darfEintrag(e.recht)),
|
||||
).map((e) =>
|
||||
modus === "DIENSTLEISTUNG" && e.beschriftungDienstleistung
|
||||
? { ...e, beschriftung: e.beschriftungDienstleistung }
|
||||
: e,
|
||||
|
||||
Reference in New Issue
Block a user