Files
Laufzettel/src/app/(app)/kein-zugriff/page.tsx
T
Florian WildeandClaude Opus 5 0158d0292f
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
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>
2026-10-06 07:32:54 +02:00

75 lines
3.3 KiB
TypeScript

import Link from "next/link";
import { ShieldAlert } from "lucide-react";
import { ROLLEN_TEXT } from "@/lib/rollen-text";
import { sitzungErforderlich } from "@/server/auth/sitzungspflicht";
import { startseite } from "@/server/auth/rollen";
/**
* „Dafür fehlt dir die Berechtigung“ — für jemanden, der **angemeldet** ist (#018).
*
* Vorher schickte `sitzungErforderlich` ihn auf `/anmelden?fehler=keinrecht`, und
* die Anmeldeseite leitete einen Angemeldeten sofort auf die Übersicht weiter:
* die Erklärung war unerreichbar, er stand ohne Hinweis da und wusste nicht, ob
* er sich verklickt hatte oder ob etwas kaputt ist. Der Kommentar in
* `sitzungspflicht.ts` versprach „eine Weiterleitung mit Erklärung“ — die
* Erklärung fehlte.
*
* Die Seite sagt deshalb drei Dinge: dass es an der Rolle liegt, welche Rolle er
* hat, und wohin er stattdessen kann. Sie nennt **nicht**, welche Rolle nötig
* wäre — das ist eine Auskunft über die Rechteverteilung, und wer sie braucht,
* fragt den Administrator.
*/
export default async function KeinZugriffSeite({
searchParams,
}: {
searchParams: Promise<Record<string, string | string[] | undefined>>;
}) {
const { benutzer } = await sitzungErforderlich(undefined, "/kein-zugriff");
const parameter = await searchParams;
const roh = parameter["bereich"];
const bereich = (Array.isArray(roh) ? roh[0] : roh)?.trim();
// Nur eigene Pfade anzeigen: ein fremder Wert in der Adresse waere sonst ein
// Text, den jemand anderes auf diese Seite schreibt.
const gezeigt = bereich?.startsWith("/") && !bereich.startsWith("//") ? bereich : undefined;
const einstieg = startseite(benutzer.rolle);
return (
<div className="mx-auto flex max-w-prose flex-col items-start gap-5 py-10">
<ShieldAlert className="size-8 text-faellig" strokeWidth={1.75} aria-hidden="true" />
<h1 className="text-2xl font-bold">Dafür fehlt dir die Berechtigung</h1>
<p className="text-nebentext">
{gezeigt ? (
<>
Der Bereich <span className="mono break-all">{gezeigt}</span> ist für deine Rolle nicht
freigegeben.
</>
) : (
"Dieser Bereich ist für deine Rolle nicht freigegeben."
)}{" "}
Du bist angemeldet als <strong>{benutzer.name ?? benutzer.email}</strong> mit der Rolle{" "}
<strong>{ROLLEN_TEXT[benutzer.rolle]}</strong>.
</p>
<p className="text-sm text-nebentext">
In der Navigation steht nur, was du darfst — diese Meldung kommt meist von einem Link, den jemand
weitergegeben hat. Brauchst du den Bereich wirklich, wende dich an einen Administrator. Kommt deine
Rolle vom Anmeldeanbieter, wird sie dort in der Gruppenzuordnung geändert und bei der nächsten
Anmeldung übernommen.
</p>
{/*
* Der Knopf fuehrt zum **eigenen** Einstieg und nicht stur auf `/`. Fuer
* einen `MITARBEITER` war das eine Schleife: `/` verlangt `lesen`, also
* landete er wieder hier (#035).
*/}
<Link
href={einstieg}
className="flex min-h-11 w-fit items-center rounded-pille bg-akzent px-5 text-sm font-semibold text-auf-akzent md:min-h-10"
>
{einstieg === "/" ? "Zur Übersicht" : "Zu meinen Geräten"}
</Link>
</div>
);
}