feat(personen): Personen und Self-Service Meine Schluessel (#013)
Personen lassen sich anlegen, bearbeiten und archivieren; ihr Detail zeigt ihre Schluessel. Unter "Meine Schluessel" hinterlegt jede Rolle eigene Public Keys (Freigabe Frage 4), wirksam erst nach Zuweisung durch einen Operator oder Admin. Warum so: - Die eigene Person entsteht beim ersten Aufruf oder wird verknuepft: per benutzerId, sonst per E-Mail - aber nur, wenn der OIDC-Anbieter die Adresse bestaetigt hat. Dafuer merkt sich Benutzer jetzt emailBestaetigt (Migration); sonst koennte jemand mit selbst gewaehlter Adresse die Keys einer fremden Person sehen. - Die Self-Service-Services nehmen nur den angemeldeten Benutzer und leiten die Person selbst ab; fremde Schluessel-IDs gelten als nicht vorhanden. - Entfernen nur fuer eigene, selbst hinterlegte Keys ohne Verwendung, bedingt geloescht, damit eine gleichzeitige Zuweisung nicht per Cascade verschwindet. - Archivieren statt Loeschen: wer bis wann welchen Key hatte, bleibt nachvollziehbar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { fehlerZustand, textfeld, type FormularZustand } from "@/server/actions/formular";
|
||||
import { rechtErforderlich } from "@/server/auth/sitzungspflicht";
|
||||
import { eigenenPublicKeyHinterlegen, eigenenSchluesselEntfernen } from "@/server/services/meine-schluessel";
|
||||
|
||||
/**
|
||||
* Self-Service (#013). Recht `eigeneSchluesselHinterlegen` hat jede Rolle; der Service
|
||||
* arbeitet ausschliesslich mit der Person des angemeldeten Kontos.
|
||||
*/
|
||||
export type HinterlegenZustand = FormularZustand & { readonly erfolg?: string };
|
||||
|
||||
export async function eigenenKeyHinterlegenAktion(
|
||||
_vorher: HinterlegenZustand,
|
||||
formular: FormData,
|
||||
): Promise<HinterlegenZustand> {
|
||||
try {
|
||||
const { benutzer } = await rechtErforderlich("eigeneSchluesselHinterlegen");
|
||||
const neu = await eigenenPublicKeyHinterlegen(benutzer, {
|
||||
name: textfeld(formular, "name") ?? "",
|
||||
publicKey: textfeld(formular, "publicKey") ?? "",
|
||||
});
|
||||
revalidatePath("/meine-schluessel");
|
||||
return { erfolg: `Hinterlegt: ${neu.fingerprint}` };
|
||||
} catch (fehler) {
|
||||
return fehlerZustand(fehler);
|
||||
}
|
||||
}
|
||||
|
||||
export async function eigenenKeyEntfernenAktion(
|
||||
_vorher: FormularZustand,
|
||||
formular: FormData,
|
||||
): Promise<FormularZustand> {
|
||||
try {
|
||||
const { benutzer } = await rechtErforderlich("eigeneSchluesselHinterlegen");
|
||||
await eigenenSchluesselEntfernen(benutzer, textfeld(formular, "id") ?? "");
|
||||
revalidatePath("/meine-schluessel");
|
||||
return {};
|
||||
} catch (fehler) {
|
||||
return fehlerZustand(fehler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
import type { PersonEingabe } from "@/lib/personen";
|
||||
import { akteurVon, fehlerZustand, textfeld, type FormularZustand } from "@/server/actions/formular";
|
||||
import { rechtErforderlich } from "@/server/auth/sitzungspflicht";
|
||||
import { personAnlegen, personArchivieren, personBearbeiten } from "@/server/services/personen";
|
||||
|
||||
/**
|
||||
* Formular-Aktionen fuer Personen (#013). Personen pflegt, wer Schluessel verwaltet (Admin,
|
||||
* Operator) – eine Person ist die Klammer um Personen-Keys.
|
||||
*/
|
||||
|
||||
function personFelder(formular: FormData): PersonEingabe {
|
||||
return {
|
||||
name: textfeld(formular, "name") ?? "",
|
||||
email: textfeld(formular, "email"),
|
||||
organisation: textfeld(formular, "organisation"),
|
||||
notiz: textfeld(formular, "notiz"),
|
||||
benutzerId: textfeld(formular, "benutzerId"),
|
||||
};
|
||||
}
|
||||
|
||||
export async function personAnlegenAktion(
|
||||
_vorher: FormularZustand,
|
||||
formular: FormData,
|
||||
): Promise<FormularZustand> {
|
||||
let id: string;
|
||||
try {
|
||||
const { benutzer } = await rechtErforderlich("schluesselVerwalten");
|
||||
({ id } = await personAnlegen(personFelder(formular), akteurVon(benutzer)));
|
||||
} catch (fehler) {
|
||||
return fehlerZustand(fehler);
|
||||
}
|
||||
revalidatePath("/personen");
|
||||
redirect(`/personen/${id}`);
|
||||
}
|
||||
|
||||
export async function personBearbeitenAktion(
|
||||
_vorher: FormularZustand,
|
||||
formular: FormData,
|
||||
): Promise<FormularZustand> {
|
||||
const id = textfeld(formular, "id") ?? "";
|
||||
try {
|
||||
const { benutzer } = await rechtErforderlich("schluesselVerwalten");
|
||||
await personBearbeiten(id, personFelder(formular), akteurVon(benutzer));
|
||||
} catch (fehler) {
|
||||
return fehlerZustand(fehler);
|
||||
}
|
||||
revalidatePath("/personen", "layout");
|
||||
redirect(`/personen/${id}`);
|
||||
}
|
||||
|
||||
export async function personArchivierenAktion(
|
||||
_vorher: FormularZustand,
|
||||
formular: FormData,
|
||||
): Promise<FormularZustand> {
|
||||
const id = textfeld(formular, "id") ?? "";
|
||||
try {
|
||||
const { benutzer } = await rechtErforderlich("schluesselVerwalten");
|
||||
await personArchivieren(id, akteurVon(benutzer));
|
||||
} catch (fehler) {
|
||||
return fehlerZustand(fehler);
|
||||
}
|
||||
revalidatePath("/personen", "layout");
|
||||
redirect("/personen");
|
||||
}
|
||||
Reference in New Issue
Block a user