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,103 @@
|
||||
import type { Prisma } from "@/generated/prisma/client";
|
||||
import { prisma } from "@/lib/db";
|
||||
|
||||
/**
|
||||
* Lesemodelle fuer Personen (#013). Jede angemeldete Rolle darf sie sehen; Schluessel nur mit
|
||||
* festem `select` ohne Chiffrat-Felder.
|
||||
*/
|
||||
|
||||
export type PersonFilter = { readonly q?: string; readonly archivierte?: boolean };
|
||||
|
||||
export async function personenListe(filter: PersonFilter = {}) {
|
||||
const q = filter.q?.trim().slice(0, 100);
|
||||
const where: Prisma.PersonWhereInput = {
|
||||
...(filter.archivierte ? {} : { archiviertAm: null }),
|
||||
...(q
|
||||
? {
|
||||
OR: [
|
||||
{ name: { contains: q, mode: "insensitive" } },
|
||||
{ email: { contains: q, mode: "insensitive" } },
|
||||
{ organisation: { contains: q, mode: "insensitive" } },
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
const personen = await prisma.person.findMany({
|
||||
where,
|
||||
orderBy: { name: "asc" },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
organisation: true,
|
||||
archiviertAm: true,
|
||||
benutzer: { select: { id: true, name: true } },
|
||||
_count: { select: { schluessel: { where: { status: "AKTIV" } } } },
|
||||
},
|
||||
});
|
||||
return personen.map(({ _count, ...rest }) => ({ ...rest, aktiveSchluessel: _count.schluessel }));
|
||||
}
|
||||
|
||||
export type PersonListenEintrag = Awaited<ReturnType<typeof personenListe>>[number];
|
||||
|
||||
export async function personDetail(id: string) {
|
||||
const person = await prisma.person.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
organisation: true,
|
||||
notiz: true,
|
||||
archiviertAm: true,
|
||||
angelegtAm: true,
|
||||
benutzerId: true,
|
||||
benutzer: { select: { id: true, name: true, email: true, rolle: true } },
|
||||
schluessel: {
|
||||
orderBy: [{ status: "asc" }, { name: "asc" }],
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
typ: true,
|
||||
kategorie: true,
|
||||
status: true,
|
||||
publicKey: true,
|
||||
fingerprint: true,
|
||||
ablaufAm: true,
|
||||
selbstHinterlegt: true,
|
||||
_count: {
|
||||
select: {
|
||||
publicVerwendungen: { where: { zustand: { not: "ENTFERNT" } } },
|
||||
privateVerwendungen: { where: { zustand: { not: "ENTFERNT" } } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!person) return null;
|
||||
return {
|
||||
...person,
|
||||
schluessel: person.schluessel.map(({ _count, ...rest }) => ({
|
||||
...rest,
|
||||
verwendungen: _count.publicVerwendungen + _count.privateVerwendungen,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
export type PersonDetail = NonNullable<Awaited<ReturnType<typeof personDetail>>>;
|
||||
|
||||
/**
|
||||
* Konten, die sich mit einer Person verknuepfen lassen: noch keiner Person zugeordnet, oder
|
||||
* der Person `personId` selbst. Der Notfall-Admin ist kein Mensch mit eigenen Keys.
|
||||
*/
|
||||
export async function kontenAuswahl(personId?: string) {
|
||||
return prisma.benutzer.findMany({
|
||||
where: {
|
||||
istNotfall: false,
|
||||
OR: [{ person: null }, ...(personId ? [{ person: { id: personId } }] : [])],
|
||||
},
|
||||
orderBy: { name: "asc" },
|
||||
select: { id: true, name: true, email: true },
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user