feat(selbstauskunft): Mitarbeiter sehen ihre eigenen Geraete (#55)
„Was ist eigentlich mit meinem Notebook passiert?“ musste bisher ein Techniker nachsehen. Ein Mitarbeiter kann die Arbeiten an **seinen** Geraeten jetzt selbst ansehen — mit der fuenften Rolle MITARBEITER, zugeordnet beim Anmelden ueber den UPN. Was er sieht, sind Titel, Zeitraum und Status. Keine Schritte, keine Notizen, keine Kommentare, keine Anhaenge: in einem Lauf stehen Arbeitsnotizen ueber Menschen und Befehle mit Platzhaltern fuer Zugangsdaten. Eine Selbstauskunft ist keine Akteneinsicht, und das haelt nicht die Seite fest, sondern das Lesemodell — eine Seite, die filtert, ist eine Seite, die es vergessen kann. `selbstauskunft` ist ein **Ersatz** fuer `lesen` und kein Zusatz; auch ein ADMIN hat es nicht. Ein zweiter Weg zu denselben Daten waere eine zweite Stelle, an der jemand die Einschraenkung auf die eigene Person vergisst. Die Datenbank verlangt das sogar: ein CHECK haelt fest, dass Rolle und Zuordnung zusammengehoeren, in beide Richtungen. Ein mehrdeutiger UPN wird abgewiesen und protokolliert. Zwei Kunden koennen dieselbe Person fuehren; ein findFirst entschiede nach Sortierung darueber, wessen Geraete jemand sieht. **Verhaltensaenderung:** wer sich anmeldet und in keiner Rollengruppe steht, bekommt nicht mehr automatisch LESER — also nicht mehr Lesezugriff auf alle Laeufe des Hauses. Das greift nur mit konfigurierten OIDC_GROUP_*; ohne sie bleibt alles wie vorher, denn ohne abgebildete Gruppen laesst sich Techniker nicht von Mitarbeiter unterscheiden. Nebenbei behoben: `staerksteRolle` benutzte LESER als Startwert und damit als Untergrenze — mit einer Rolle darunter lieferte sie mehr Rechte als uebergeben. Gefunden von einem Test, der seit #007 unveraendert dasteht. Closes #55 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a417426f43
commit
0158d0292f
+49
-8
@@ -22,13 +22,26 @@ import { datenbankLeeren, datenbankVerfuegbar, testPrisma } from "./helpers/db";
|
||||
|
||||
const UMGEBUNG = { ...process.env };
|
||||
|
||||
/**
|
||||
* Standard-Anspruechge **mit** einer Rollengruppe.
|
||||
*
|
||||
* Seit #035 ist „angemeldet, aber in keiner Rollengruppe“ kein stilles `LESER`
|
||||
* mehr, sondern fuehrt in die Selbstauskunft — und ohne passende Person im
|
||||
* Verzeichnis wird die Anmeldung abgewiesen. Die Tests hier pruefen
|
||||
* JIT-Provisioning, Protokoll und Kundenbindung; sie sollen nicht nebenbei an
|
||||
* dieser Weiche haengen. Wer die Weiche selbst prueft, findet das in
|
||||
* `tests/selbstauskunft.test.ts`.
|
||||
*/
|
||||
function anspruecke(ueberschreiben: Partial<OidcAnspruecke> = {}): OidcAnspruecke {
|
||||
return {
|
||||
sub: "anbieter-sub-1",
|
||||
email: "maria.huber@firma.example",
|
||||
emailBestaetigt: true,
|
||||
name: "Maria Huber",
|
||||
gruppen: [],
|
||||
gruppen: ["lz-techniker"],
|
||||
// Der UPN haengt an der Selbstauskunft (#035); im AD ist er oft nicht die
|
||||
// Mailadresse, deshalb hier bewusst ein anderer Wert.
|
||||
upn: "mhuber@firma.local",
|
||||
ziel: "/",
|
||||
...ueberschreiben,
|
||||
};
|
||||
@@ -136,9 +149,23 @@ describe.runIf(datenbankVerfuegbar)("Anmeldung und Benutzerpflege", () => {
|
||||
const befoerdert = await benutzerAusOidcUebernehmen(anspruecke({ gruppen: ["lz-admin"] }));
|
||||
expect(befoerdert.rolle).toBe("ADMIN");
|
||||
|
||||
// Und wieder zurueck: eine entzogene Gruppe entzieht die Rolle.
|
||||
const entzogen = await benutzerAusOidcUebernehmen(anspruecke({ gruppen: [] }));
|
||||
expect(entzogen.rolle).toBe("LESER");
|
||||
/*
|
||||
* Und wieder zurueck. **Seit #035 ist das kein `LESER` mehr:** wem die
|
||||
* Rollengruppe entzogen wird und wer zu keiner Person im Verzeichnis
|
||||
* passt, kommt nicht mehr herein.
|
||||
*
|
||||
* Das ist strenger als vorher und Absicht. Vorher behielt ein
|
||||
* ausgeschiedener Techniker Lesezugriff auf **alle** Laeufe des Hauses,
|
||||
* solange niemand sein Konto deaktivierte — und genau das vergisst man.
|
||||
* Der Notfallzugang dagegen bleibt: `AUTH_LOCAL_ENABLED` haengt nicht an
|
||||
* den Gruppen.
|
||||
*/
|
||||
await expect(benutzerAusOidcUebernehmen(anspruecke({ gruppen: [] }))).rejects.toThrow(AnmeldeFehler);
|
||||
|
||||
// Die Rolle in der Datenbank bleibt dabei, was sie war – abgewiesen heisst
|
||||
// abgewiesen, nicht herabgestuft.
|
||||
const unveraendert = await testPrisma.benutzer.findUniqueOrThrow({ where: { id: erst.id } });
|
||||
expect(unveraendert.rolle).toBe("ADMIN");
|
||||
});
|
||||
|
||||
it("protokolliert jede Rollenaenderung aus den Gruppen", async () => {
|
||||
@@ -195,12 +222,22 @@ describe.runIf(datenbankVerfuegbar)("Anmeldung und Benutzerpflege", () => {
|
||||
});
|
||||
|
||||
describe("Kundenbindung", () => {
|
||||
/*
|
||||
* **Rollengruppe und Kundengruppe sind zwei Dinge** (#035). Diese Tests gaben
|
||||
* vorher nur `lz-kunde-MUS` mit und bekamen dadurch stillschweigend `LESER`.
|
||||
* Seit #035 fuehrt „keine Rollengruppe“ in die Selbstauskunft — eine
|
||||
* Kundengruppe allein sagt eben nichts darueber, was jemand duerfen soll.
|
||||
* Wer lesen soll, gehoert in die Leser-Gruppe; dafuer gibt es
|
||||
* `OIDC_GROUP_VIEWER`.
|
||||
*/
|
||||
it("bindet den Benutzer an die Kunden aus seinen Gruppen", async () => {
|
||||
process.env["OIDC_CUSTOMER_GROUP_PREFIX"] = "lz-kunde-";
|
||||
const mus = await testPrisma.kunde.create({ data: { name: "Musterfirma", kuerzel: "MUS" } });
|
||||
await testPrisma.kunde.create({ data: { name: "Baeckerei Reiter", kuerzel: "REI" } });
|
||||
|
||||
const benutzer = await benutzerAusOidcUebernehmen(anspruecke({ gruppen: ["lz-kunde-MUS"] }));
|
||||
const benutzer = await benutzerAusOidcUebernehmen(
|
||||
anspruecke({ gruppen: ["lz-leser", "lz-kunde-MUS"] }),
|
||||
);
|
||||
const bindungen = await testPrisma.benutzerKunde.findMany({
|
||||
where: { benutzerId: benutzer.id },
|
||||
});
|
||||
@@ -215,8 +252,10 @@ describe.runIf(datenbankVerfuegbar)("Anmeldung und Benutzerpflege", () => {
|
||||
data: { name: "Baeckerei Reiter", kuerzel: "REI" },
|
||||
});
|
||||
|
||||
const benutzer = await benutzerAusOidcUebernehmen(anspruecke({ gruppen: ["lz-kunde-MUS"] }));
|
||||
await benutzerAusOidcUebernehmen(anspruecke({ gruppen: ["lz-kunde-REI"] }));
|
||||
const benutzer = await benutzerAusOidcUebernehmen(
|
||||
anspruecke({ gruppen: ["lz-leser", "lz-kunde-MUS"] }),
|
||||
);
|
||||
await benutzerAusOidcUebernehmen(anspruecke({ gruppen: ["lz-leser", "lz-kunde-REI"] }));
|
||||
|
||||
const bindungen = await testPrisma.benutzerKunde.findMany({
|
||||
where: { benutzerId: benutzer.id },
|
||||
@@ -227,7 +266,9 @@ describe.runIf(datenbankVerfuegbar)("Anmeldung und Benutzerpflege", () => {
|
||||
|
||||
it("bindet ohne Praefix niemanden ein", async () => {
|
||||
await testPrisma.kunde.create({ data: { name: "Musterfirma", kuerzel: "MUS" } });
|
||||
const benutzer = await benutzerAusOidcUebernehmen(anspruecke({ gruppen: ["lz-kunde-MUS"] }));
|
||||
const benutzer = await benutzerAusOidcUebernehmen(
|
||||
anspruecke({ gruppen: ["lz-leser", "lz-kunde-MUS"] }),
|
||||
);
|
||||
expect(await testPrisma.benutzerKunde.count({ where: { benutzerId: benutzer.id } })).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user