Authorization Code mit PKCE, State und Nonce über openid-client 6.8.8; die Rolle kommt bei jeder Anmeldung aus dem Gruppen-Claim. Lokale Konten werden nie über den Namen übernommen, verknüpfen geht nur angemeldet. Getestet gegen einen Mock-IdP inklusive gefälschtem State, falscher Nonce und fremder Audience. Closes #016 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
3.2 KiB
TypeScript
71 lines
3.2 KiB
TypeScript
import type { Metadata } from "next";
|
||
import { oeffentlicheAdresse } from "@/lib/adressen";
|
||
import { Karte } from "@/components/karte";
|
||
import { Status } from "@/components/status";
|
||
import { oidcEinstellungen } from "@/server/auth/oidc";
|
||
import { darf } from "@/server/auth/rechte";
|
||
import { seiteSchuetzen } from "@/server/auth/zugang";
|
||
import { benutzerListe } from "@/server/services/benutzer";
|
||
import { schluesselFuer } from "@/server/services/schluessel";
|
||
import { tokensVon } from "@/server/services/tokens";
|
||
import { BenutzerVerwaltung } from "./benutzer-verwaltung";
|
||
import { PasswortAendern } from "./passwort-aendern";
|
||
import { SchluesselVerwaltung } from "./schluessel-verwaltung";
|
||
import { TokenVerwaltung } from "./token-verwaltung";
|
||
|
||
export const metadata: Metadata = { title: "Einstellungen" };
|
||
|
||
/** Einstellungen: eigene SSH-Schlüssel, CLI-Tokens, Passwort; für Admins die Benutzer. */
|
||
export default async function Einstellungen({ searchParams }: { searchParams: Promise<{ sso?: string }> }) {
|
||
const benutzer = await seiteSchuetzen("eigeneZugaenge", "/einstellungen");
|
||
const sso = oidcEinstellungen();
|
||
const ssoFehler = (await searchParams).sso;
|
||
const admin = darf(benutzer.rolle, "benutzerVerwalten");
|
||
return (
|
||
<div className="flex flex-col gap-6">
|
||
<header>
|
||
<h1 className="text-2xl font-semibold tracking-tight sm:text-3xl">Einstellungen</h1>
|
||
<p className="text-muted text-sm">
|
||
Angemeldet als {benutzer.benutzername} · {benutzer.rolle === "ADMIN" ? "Admin" : "Operator"}
|
||
</p>
|
||
</header>
|
||
<SchluesselVerwaltung anfang={await schluesselFuer(benutzer)} teamErlaubt={admin} />
|
||
<TokenVerwaltung
|
||
anfang={await tokensVon(benutzer.id)}
|
||
adresse={oeffentlicheAdresse("/", "http://localhost:3000").origin}
|
||
/>
|
||
{benutzer.passwortHash ? <PasswortAendern /> : null}
|
||
{sso ? (
|
||
<Karte titel="Anmeldung per SSO">
|
||
<div className="flex flex-col gap-3">
|
||
{ssoFehler === "schon_verknuepft" ? (
|
||
<Status art="fehler">Dieses SSO-Konto gehört schon zu einem anderen Benutzer.</Status>
|
||
) : null}
|
||
{benutzer.oidcSubjekt ? (
|
||
<Status art="erledigt">
|
||
Mit {sso.anzeigename} verknüpft – die Rolle kommt bei jeder SSO-Anmeldung aus den Gruppen.
|
||
</Status>
|
||
) : (
|
||
<>
|
||
<p className="text-muted text-sm">
|
||
Dein Konto ist noch nicht mit {sso.anzeigename} verknüpft. Danach kannst du dich auch dort
|
||
anmelden; die Rolle kommt dann aus den Gruppen beim Identity Provider.
|
||
</p>
|
||
<div>
|
||
<a
|
||
href="/api/v1/auth/oidc/start?verknuepfen=1"
|
||
className="border-linie bg-panel-hoch text-text hover:border-muted inline-flex h-11 items-center rounded-lg border px-4 text-sm font-semibold sm:h-10"
|
||
>
|
||
Mit {sso.anzeigename} verknüpfen
|
||
</a>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
</Karte>
|
||
) : null}
|
||
{admin ? <BenutzerVerwaltung anfang={await benutzerListe()} ich={benutzer.benutzername} /> : null}
|
||
</div>
|
||
);
|
||
}
|