# #035 M22 – Mitarbeiter-Selbstauskunft **Status:** erledigt **Im Tracker:** [#55](https://git.wilde-server.de/florian.wilde/Laufzettel/issues/55) **Meilenstein:** v0.2.0 **Labels:** phase-2, backend, frontend **Geschätzt:** 8 h **Tatsächlich:** 2 h 50 min ## Ziel „Was ist eigentlich mit meinem Notebook passiert?“ fragt heute jemand im Ticket, und ein Techniker sieht nach. Ein Mitarbeiter soll die Läufe zu **seinen** Geräten und zu **seiner** Person selbst ansehen können — nur lesend. ## Woran es hängt Ein `Mitarbeiter` ist kein `Benutzer`. Die Anwendung kennt vier Rollen für Leute, die **arbeiten**; ein Mitarbeiter ist Gegenstand der Arbeit, nicht ihr Träger. Es gibt heute keinen Weg, von einer Anmeldung zu einer `Mitarbeiter`-Zeile zu kommen. ## Zu entscheiden - [x] **Anmeldung über OIDC, Zuordnung über den UPN, neue Rolle `MITARBEITER`** — Empfehlung. Das Haus hat seine Leute ohnehin im Identitätsanbieter; wer sich anmeldet und keiner Rollengruppe angehört, wird über `upn` einer `Mitarbeiter`-Zeile zugeordnet und bekommt genau ein Recht: `selbstauskunft`. `Benutzer.mitarbeiterId` hält die Zuordnung fest. Die Alternative — ein Link mit Token je Person — braucht keine Anmeldung, aber jeder weitergeleitete Link ist dann ein Datenleck, und widerrufen müsste man je Person. - [x] **Was der Mitarbeiter sieht:** Titel, Status, Zeitraum und Gerät eines Laufs. **Nicht** die Schritte, nicht die Notizen, nicht die Kommentare, nicht die Anhänge. In einem Lauf stehen Arbeitsnotizen über Menschen und Zugangsdaten-Platzhalter; eine Selbstauskunft ist keine Akteneinsicht. Das ist die wichtigste Entscheidung dieses Meilensteins und gehört in die Anleitung, nicht nur in den Code. - [x] **Kein Zugang ohne Zuordnung.** Wer sich anmeldet und zu keiner `Mitarbeiter`-Zeile passt, bekommt `/kein-zugriff` mit Begründung — nicht eine leere Seite, die wie ein Fehler aussieht. ## Aufgaben - [x] `Rolle` um `MITARBEITER` erweitern; `RECHTE` bekommt `selbstauskunft: ["MITARBEITER", …]`. **Jede** Rollenprüfung in `tests/rollen.test.ts` wächst um eine Spalte — die Matrix ist ausgeschrieben und nicht berechnet, genau damit das auffällt - [x] `Benutzer.mitarbeiterId` mit Mandantengrenze als zusammengesetzter Fremdschlüssel - [x] Zuordnung beim Login über `upn`, unabhängig von Groß- und Kleinschreibung; mehrdeutig ist ein Fehler und kein Zufallstreffer - [x] Eigene Seite `/meine-geraete`: meine Geräte, je Gerät die Läufe chronologisch, Titel und Status - [x] **Jede Abfrage schränkt auf die eigene Person ein** — nicht die Seite. Ein Lesemodell trägt seine Sicht selbst (CLAUDE.md) - [x] Die Navigation zeigt einem `MITARBEITER` nur diese Seite; alles andere ist weg, nicht grau - [x] Anleitung: ein eigenes kurzes Kapitel, an Mitarbeiter gerichtet und nicht an Techniker ## Akzeptanzkriterien - [x] Test: ein `MITARBEITER` sieht die Läufe seiner Geräte und keinen anderen - [x] Test: er sieht **keine** Schritte, Notizen, Kommentare oder Anhänge — geprüft über die Antwort, nicht über die Seite - [x] Test: er kann nichts schreiben — jede Server Action weist ihn ab (`tests/zugang.test.ts` erkennt eine ungeprüfte Aktion ohnehin) - [x] Test: ohne passende `Mitarbeiter`-Zeile führt die Anmeldung auf `/kein-zugriff` mit Begründung - [x] Test: ein mehrdeutiger UPN wird abgewiesen und protokolliert - [x] Test: die Rollenmatrix ist vollständig — jede der fünf Rollen gegen jedes Recht - [x] Am Handy kein horizontales Scrollen ## Beim Umsetzen dazugekommen - **Kein zusammengesetzter Fremdschlüssel, und zwar aus einem Grund.** Die Aufgabe verlangte `Benutzer.mitarbeiterId` „mit Mandantengrenze als zusammengesetzter Fremdschlüssel“. Das geht nicht: ein `Benutzer` trägt keine `kundeId`, gegen die man zusammensetzen könnte — ein Administrator sieht alle Kunden. Stattdessen hält die Datenbank das fest, was hier wirklich zählt: ein **CHECK**, dass Rolle und Zuordnung zusammengehören (in beide Richtungen), und ein **eindeutiger Index**, dass eine Person höchstens ein Konto hat. Die Zusage „sieht nur seine eigenen Läufe“ hängt an der einen Kennung, und stärker als ein Fremdschlüssel darauf wird sie nicht. - **Nicht `/kein-zugriff`, sondern die Anmeldeseite.** Das Kriterium sah vor, dass eine Anmeldung ohne passende Person auf `/kein-zugriff` führt. Diese Seite verlangt aber eine **Sitzung**, und bei gescheiterter Anmeldung gibt es keine — sie würde auf die Anmeldeseite zurückleiten, also im Kreis. Die Begründung steht deshalb dort, mit zwei eigenen Fehlercodes (`keine-person`, `upn-mehrdeutig`). Der Geist des Kriteriums ist damit erfüllt: keine leere Seite, die wie ein Fehler aussieht. - **Ein bestehender Test seit #007 fand eine echte Lücke.** `staerksteRolle` benutzte `LESER` als Startwert, und das war zugleich eine **Untergrenze**: `staerksteRolle(["MITARBEITER"])` ergab `LESER`, also **mehr** Rechte als übergeben. Solange alle Rollen mindestens `LESER` waren, fiel das nie auf. Der Test „gibt bei nur diese Rolle zurück“ war sofort rot. - **Jeder Navigationseintrag nennt jetzt sein Recht.** Bis hierher stand `recht` nur am Admin-Bereich — das genügte, weil jede Rolle `lesen` durfte. Mit `MITARBEITER` stimmt das nicht mehr, und ohne diese Änderung hätte die Seitenleiste ihm sechs Links angeboten, die alle auf `/kein-zugriff` führen. - **Zwei Bedienfehler beim Durchklicken gefunden**, beide dieselbe Ursache: das Standardziel nach der Anmeldung ist `/`, und die Übersicht verlangt `lesen`. Ein Mitarbeiter sah als Erstes „Dafür fehlt dir die Berechtigung“ — und der Knopf auf jener Seite führte zurück auf `/`, also im Kreis. Beides behebt `startseite(rolle)` in `src/server/auth/rollen.ts`, an einer Stelle und mit Test. - **Eine Gegenprobe blieb grün, und das war der Befund.** Der Test „sieht keine Notizen“ prüft die **Antwort**, und die ist sicher: `laufBauen` setzt sie aus den erlaubten Feldern zusammen. Als ich im `select` versuchsweise `ergebnisse: { notiz }` ergänzte, blieb er deshalb grün — während der Kopfkommentar der Datei behauptet, das `select` hole es „gar nicht erst“. Diese Behauptung hat jetzt ihren eigenen Test, der die Datei selbst liest. Ein `select`, das Notizen mitliest, zieht sie durch Protokolle und Fehlerberichte. - **`selbstauskunft` hat auch der ADMIN nicht.** Es ist ein **Ersatz** für `lesen`, kein Zusatz: ein zweiter Weg zu denselben Daten wäre eine zweite Stelle, an der jemand die Einschränkung auf die eigene Person vergisst. Wer beides braucht, braucht zwei Konten — und der CHECK verlangt das. - **Der Rang von `MITARBEITER` liegt unter `LESER`.** Der Rang entscheidet, wer gewinnt, wenn jemand in mehreren abgebildeten Gruppen steht. Stünde er darüber, verlöre ein Techniker seine Rechte, weil er auch Mitarbeiter des Hauses ist. - **Ein struktureller Test, dass keine Server Action `selbstauskunft` verlangt.** Die Zusage „kann nichts schreiben“ steht nicht in einer Prüfung, sondern in der **Abwesenheit** einer Aktion mit diesem Recht — und die muss jemand bewachen. - **Die Weiche greift nur mit eingerichteter Gruppenzuordnung.** Ohne abgebildete Gruppen kann die Anwendung nicht unterscheiden, ob jemand Techniker oder Mitarbeiter ist. Ein Update, das jeden aussperrt, dem die Gruppen fehlen, wäre ein kaputtes Update — mit eigenem Test. - **Die authentik-Doku enthielt jetzt einen falschen Satz:** „Ist der Benutzer in keiner der vier Gruppen, bekommt er `LESER`.“ Richtiggestellt, mit einem eigenen Abschnitt zur Selbstauskunft und dem Hinweis, wie man das alte Verhalten behält. - **Das Anleitungskapitel 09 ist an Mitarbeiter gerichtet**, nicht an die IT — das erste Kapitel dieser Anleitung, das sich an jemanden außerhalb der IT wendet. Es lässt sich weitergeben, ohne den Rest mitzuschicken, und sagt ausdrücklich, **warum** man die Schritte nicht sieht.