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>
65 lines
2.4 KiB
TypeScript
65 lines
2.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { navigationFuer } from "@/lib/navigation";
|
|
import { darf } from "@/server/auth/rollen";
|
|
import type { Rolle } from "@/generated/prisma/enums";
|
|
|
|
/** Was die Seitenleiste je Betriebsmodus und Rolle zeigt (#005, #007). */
|
|
|
|
function pfadeFuer(modus: "INTERN" | "DIENSTLEISTUNG", rolle: Rolle): string[] {
|
|
return navigationFuer(modus, (recht) => darf(rolle, recht)).map((e) => e.pfad);
|
|
}
|
|
|
|
describe("Seitenleiste nach Betriebsmodus", () => {
|
|
it("zeigt im Modus Intern die Kunden-Ebene nicht", () => {
|
|
const pfade = pfadeFuer("INTERN", "ADMIN");
|
|
expect(pfade).not.toContain("/kunden");
|
|
expect(pfade).toContain("/mitarbeiter");
|
|
});
|
|
|
|
it("zeigt im Modus Dienstleistung Kunden statt Mitarbeiter & Geräte", () => {
|
|
const pfade = pfadeFuer("DIENSTLEISTUNG", "ADMIN");
|
|
expect(pfade).toContain("/kunden");
|
|
expect(pfade).not.toContain("/mitarbeiter");
|
|
});
|
|
});
|
|
|
|
describe("Seitenleiste nach Rolle", () => {
|
|
it("zeigt Admin nur dem Administrator", () => {
|
|
expect(pfadeFuer("INTERN", "ADMIN")).toContain("/admin/benutzer");
|
|
for (const rolle of ["REDAKTEUR", "TECHNIKER", "LESER"] as const) {
|
|
expect(pfadeFuer("INTERN", rolle), `Rolle ${rolle}`).not.toContain("/admin/benutzer");
|
|
}
|
|
});
|
|
|
|
it("zeigt einem Leser die fachlichen Bereiche, aber nichts Verwaltendes", () => {
|
|
const pfade = pfadeFuer("INTERN", "LESER");
|
|
expect(pfade).toContain("/");
|
|
expect(pfade).toContain("/laeufe");
|
|
expect(pfade).toContain("/archiv");
|
|
expect(pfade).not.toContain("/admin/benutzer");
|
|
});
|
|
|
|
it("blendet ohne bekannte Rolle alles Rechtegebundene aus", () => {
|
|
// So sieht die Huelle aus, solange niemand angemeldet ist.
|
|
const pfade = navigationFuer("INTERN", () => false).map((e) => e.pfad);
|
|
expect(pfade).not.toContain("/admin/benutzer");
|
|
});
|
|
});
|
|
|
|
describe("Aufbau der Eintraege", () => {
|
|
it("verlinkt keinen Eintrag, dessen Seite es noch nicht gibt", () => {
|
|
for (const eintrag of navigationFuer("INTERN")) {
|
|
if (!eintrag.verfuegbar) {
|
|
expect(eintrag.ausIssue, `${eintrag.pfad} braucht eine Issue-Nummer`).toBeGreaterThan(0);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("hat keine doppelten Pfade je Modus", () => {
|
|
for (const modus of ["INTERN", "DIENSTLEISTUNG"] as const) {
|
|
const pfade = pfadeFuer(modus, "ADMIN");
|
|
expect(new Set(pfade).size).toBe(pfade.length);
|
|
}
|
|
});
|
|
});
|