Umschaltbar ueber AUTH_ENABLED in der .env. Steht sie auf false, verhaelt
sich alles wie bisher - geprueft mit den 200 bestehenden Pruefungen.
Kern ist eine Actor- und Policy-Schicht: getActor() liefert bei
ausgeschalteter Anmeldung einen system-Actor mit Vollzugriff, sonst den
angemeldeten Benutzer. Dadurch braucht keine einzige Route ein
'if (anmeldungAktiv)' - sie fragen alle nur can() bzw. requirePermission().
- Datenmodell: User, Session, Group, Membership; Codes bekommen Gruppe und
Ersteller. Die Migration legt eine Standardgruppe an und ordnet ihr
bestehende Codes zu, bevor die Spalte verpflichtend wird - sonst
scheiterte sie auf jeder bereits benutzten Installation.
- Passwoerter mit scrypt aus der Node-Standardbibliothek. Bewusst kein
Argon2: das braeuchte ein natives Modul passend zur Container-Plattform.
- Sitzungen in der Datenbank, im Cookie nur ein Zufallstoken; gespeichert
wird dessen SHA-256. Deaktivieren oder ein neues Passwort beendet alle
offenen Sitzungen sofort.
- Aussperrschutz: der letzte aktive Administrator kann sich nicht selbst
entmachten, deaktivieren oder loeschen. Der Schalter liegt in der .env,
nicht in der Oberflaeche - so bleibt der Weg zurueck immer offen.
- Ersteinrichtung legt den ersten Administrator an und schliesst sich danach.
- Die Weiterleitung /r/{slug} bleibt oeffentlich, sonst liesse sich kein
gedruckter Code mehr scannen.
Zwei Fehler dabei gefunden und behoben:
- 'return handleLogin(...)' innerhalb eines try-Blocks gibt das Promise
zurueck, bevor es sich aufloest. Die Ablehnung kam nie im catch an und
wurde zu einem nackten 500er statt einer 401. Jetzt mit await.
- Die Fehlerklassen wurden ueber instanceof erkannt. type-module.ts landet
aber sowohl im Server- als auch im Client-Buendel, es gibt also zwei
Klassenidentitaeten - saubere 400er wurden dadurch still zu 500ern. Die
Erkennung laeuft jetzt ueber eine Eigenschaft.
Geprueft: 51 neue Pruefungen fuer Anmeldung und die vollstaendige
Rollenmatrix, dazu die 200 bestehenden bei ausgeschalteter Anmeldung.
46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
import type { Metadata } from "next";
|
|
import Link from "next/link";
|
|
|
|
import { Icon } from "@/components/icons";
|
|
import { UserAdmin } from "@/components/user-admin";
|
|
import { requirePagePermission } from "@/lib/auth/guard";
|
|
import { MIN_PASSWORD_LENGTH } from "@/lib/auth/password";
|
|
|
|
export const metadata: Metadata = { title: "Benutzer" };
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export default async function UsersPage() {
|
|
const actor = await requirePagePermission("instance:manage");
|
|
|
|
return (
|
|
<div className="mx-auto max-w-3xl px-4 py-8 sm:px-6 sm:py-10">
|
|
<Link
|
|
href="/verwaltung"
|
|
className="mb-5 inline-flex items-center gap-1.5 rounded-lg text-sm font-medium text-slate-600 transition-colors hover:text-slate-900"
|
|
>
|
|
<Icon name="arrowLeft" className="size-4" />
|
|
Verwaltung
|
|
</Link>
|
|
|
|
<div className="mb-7">
|
|
<h1 className="text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl">
|
|
Benutzer
|
|
</h1>
|
|
<p className="mt-2 text-slate-600">
|
|
Konten anlegen, Passwörter setzen und Zugänge deaktivieren. Wer in
|
|
welcher Gruppe mitarbeitet, legst du unter{" "}
|
|
<Link href="/verwaltung/gruppen" className="font-medium underline">
|
|
Gruppen
|
|
</Link>{" "}
|
|
fest.
|
|
</p>
|
|
</div>
|
|
|
|
<UserAdmin
|
|
minPasswordLength={MIN_PASSWORD_LENGTH}
|
|
currentUserId={actor.kind === "user" ? actor.user.id : null}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|