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:
@@ -72,6 +72,15 @@ Dreizeiler: Recht prüfen, Zod-Schema anwenden, Service rufen.
|
||||
`INTERN` immer die eigene Organisation und **verwirft** eine mitgeschickte `kundeId`.
|
||||
- **Rechte:** nur serverseitig, aus der `RECHTE`-Tabelle über `darf()` / `rechtErforderlich()`.
|
||||
Rolle und Aktiv-Status werden pro Anfrage **aus der Datenbank** gelesen, nicht aus dem Token.
|
||||
**Jede Seite prüft selbst** — die Hülle unter `src/app/(app)/layout.tsx` ist das zweite Netz,
|
||||
nicht das erste: Next rendert Layouts bei Navigation innerhalb desselben Layouts nicht neu.
|
||||
**Route Handler deckt die Hülle nicht ab**; alles unter `src/app/api/`, das mehr tut als Auth
|
||||
und Healthcheck, ruft selbst `rechtErforderlich()`. Der Proxy prüft das Cookie absichtlich nur
|
||||
auf Vorhandensein, nie inhaltlich.
|
||||
- **Server-Module gehören nicht ins Browser-Bündel.** Eine Client-Komponente importiert nichts aus
|
||||
`src/server/*`. Was beide Seiten brauchen (Beschriftungen, Formate), liegt in `src/lib` —
|
||||
`ROLLEN_TEXT` etwa in `src/lib/rollen-text.ts`, während `RECHTE` und `darf()` serverseitig
|
||||
bleiben.
|
||||
- **`tagCode`:** wird nie geändert und nie wiederverwendet, auch nicht nach Archivierung.
|
||||
- **Löschen:** Geräte, Abteilungen und Vorlagen werden archiviert. Mitarbeiter werden nur
|
||||
`aktiv = false`. Nichts wird gelöscht, woran ein Lauf hängt.
|
||||
@@ -115,7 +124,12 @@ Build-Skripte von Abhängigkeiten sind in `pnpm-workspace.yaml` unter `allowBuil
|
||||
## Prisma 7
|
||||
|
||||
Generator `prisma-client` mit Pflicht-`output` nach `src/generated/prisma`, `datasource` ohne
|
||||
`url`, Zugriff über `@prisma/adapter-pg`. Ein Adapter ist **Pflicht** — `new PrismaClient()` ohne
|
||||
`url`, Zugriff über `@prisma/adapter-pg`.
|
||||
|
||||
**Importpfade, einmal richtig:** Modelltypen kommen aus `@/generated/prisma/client`
|
||||
(`import type { Benutzer } from "@/generated/prisma/client"`), Enum-**Werte** aus
|
||||
`@/generated/prisma/enums`. `@/generated/prisma/models` exportiert nur die Hilfstypen der Abfragen,
|
||||
nicht die Modelle selbst — ein `import` von dort schlägt fehl. Ein Adapter ist **Pflicht** — `new PrismaClient()` ohne
|
||||
`adapter` wirft. Seed steht in `prisma.config.ts` unter `migrations.seed`. Der Client wird über
|
||||
den Lazy-Proxy in `src/lib/db.ts` geholt, damit `next build` ohne `DATABASE_URL` läuft.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user