Kind folgt Anker: Wie das Rechtemodell die ganze Anwendung durchdrang
Der letzte Werkstattbericht endete mit einer Frage, die geklärt schien: Wem gehört die Post? Kontakte, Firmen und Leads hatten ihr Rechtemodell, die Gruppen brachten ihr Team mit, und ein Büro mit dreißig Leuten konnte endlich Ordnung halten. Dann kam der Blick auf den Rest der Anwendung — und der Rest war groß. Objekte, Anbieter, Portale, Websites, Vorlagen, Backups, E-Mail-Vorlagen, Medienordner: alles Daten, die ein Büro genauso trennen will wie seine Kontakte. Und alles Daten, die bisher jeder sehen konnte, der die Anwendung öffnen durfte.
Wir hätten das Modell Typ für Typ nachziehen können. Stattdessen haben wir eine Woche lang etwas Grundsätzlicheres gebaut.
Wer darf das sehen? Die falsche Frage
Wer einen Kontakt sehen darf, darf noch lange nicht seine Notizen sehen. Wer ein Objekt nicht sehen darf, darf es auch nicht über die Hintertür eines Bildes oder einer Portalfreigabe erraten. Bisher haben wir diese Frage für jeden Datentyp einzeln beantwortet — und bei jedem neuen Typ neu. Das ist der Moment, in dem Rechtemodelle Löcher bekommen: nicht bei den großen Dingen, sondern beim Nebenbei, beim Anhang, bei der Zeile, an die niemand gedacht hat.
Die richtige Frage lautet nicht „Wer darf das sehen?“, sondern „Wem folgt das hier?“. Und diese Frage beantwortet jetzt jeder Datentyp selbst, im Code, direkt an seiner Klasse.
Drei Rollen, keine Ausnahme
Rechteträger sind die Wurzeln: Kontakte, Objekte, Anbieter, Portale, Websites, Vorlagen, Backups, E-Mail-Vorlagen, CRM-Ordner. Sie tragen Team und Benutzer, sie lassen sich in Gruppenbäume einsortieren, ihre Sichtbarkeit steuern die Datenbereiche der Rolle.
Kinder folgen ihrem Anker. Eine Notiz gehört zum Kontakt, ein Bild zum Objekt, eine Seite zur Website, eine Datei zu ihrem Ordner. Sie tragen keine eigenen Rechte — sie erben die Sichtbarkeit ihres Ankers, über beliebig viele Stufen. Sieht man den Anker nicht, sieht man das Kind nicht.
Bewusst frei sind nur die Typen, bei denen wir das ausdrücklich so wollen: Nachschlagelisten, Systemtabellen, das, was jeder braucht.
Und hier kommt der Teil, der uns ruhig schlafen lässt: Ein Datentyp ohne Erklärung bricht den Build. Ein automatischer Wächter prüft, dass jede der 74 Entitäten genau eine dieser Rollen trägt. Er prüft außerdem, dass Querverweise nur dort als echte Datenbankreferenz existieren, wo die Sichtbarkeit garantiert ist. Alles andere reist als loser Schlüssel und löst sich tolerant auf: Was man nicht sehen darf, erscheint als „(geschützt)“ — nicht als leere Liste und nicht als Absturz. Wer nächstes Jahr die Hausverwaltung dazubaut, kommt an dieser Entscheidung nicht vorbei. Nicht, weil es in einer Richtlinie steht, sondern weil der Compiler es verlangt.
Eine Stelle, die alles weiß
Der Server setzt die Regel an genau einem Ort durch: im Lesetunnel, durch den jede Abfrage des Desktop-Clients läuft. Für jeden Datentyp kennt er die Kette bis zur Wurzel und hängt die Rechteprüfung der Wurzel an jede Abfrage. Ein Bild, das an einer Anlage hängt, die an einem Objekt hängt, wird mit genau der Regel gefiltert, die für das Objekt gilt — drei Stufen, ein Filter, und kein Entwickler muss daran denken. Beim Schreiben gilt dasselbe Tor: Ein Kind lässt sich nur dort anlegen oder verschieben, wo der Anker es erlaubt.
Für die Website haben wir dabei eine Entscheidung bewusst einfach gehalten: Die Rechte der Website gelten für alle ihre Seiten, Menüs und Medien. Wir trennen darunter nicht noch einmal. Und die Medienordner im CRM bekamen ein eigenes Schild: Ein Ordner ist vertraulich, bis jemand ihn ausdrücklich für die Website freigibt — und diese Freigabe vererbt sich nicht. Ein neuer Unterordner unter einem freigegebenen Ordner bleibt vertraulich.
Zwei Fundstücke aus dem Livetest
Das erste Fundstück lieferte der Ordnerbaum. Ein Team weist einen Ordner sich selbst zu — und für alle anderen stürzte der komplette Medienbereich ab. Die Ursache saß in einem Detail der Datenbankschicht: Die Suche nach dem Elternordner lief über die allgemeine Ordnertabelle, die der Tunnel nicht filtert. Sie fand den Schlüssel des verborgenen Ordners, und erst das Nachladen der eigentlichen Zeile wurde verweigert — was die Datenbankschicht nicht als „darfst du nicht“, sondern als „gibt es nicht“ meldete. Jetzt löst sich ein verborgener Eltern schlicht zu nichts auf, und der verwaiste Unterordner steht unter seinem eigenen Namen oben im Baum. Ein Test stellt genau diesen Fall nach.
Das zweite Fundstück war eine Konsequenz des ersten Werkstattberichts. Jeder neue Datensatz eines Rechteträgers bekommt seinen Ersteller als Zuweisung, damit ein Benutzer mit „nur eigene“ seine Zeile weiter sieht. Klingt richtig — bis der Medienordner eines ganzen Mandanten beim ersten Upload eines Mitarbeiters entstand und damit ihm gehörte. Jeder Kollege aus einem anderen Team sah eine leere Bibliothek. Der Wurzelordner gehört jetzt dem Mandanten und niemandem sonst, und er wehrt jede Zuweisung ab.
Beide Funde kamen aus Stichproben mit echten Rollen, keine zwei Stunden nach dem Ausrollen. Genau dafür testen wir am lebenden Objekt.
Die Bilanz in Zahlen
- 361 Dateien geändert, rund 5.100 Zeilen neu und rund 3.400 Zeilen entfernt
- 74 Datentypen mit erklärter Rechte-Rolle
- 6 neue Gruppenbäume in Navigator-Bereichen, die bisher leer waren — mit Ziehen und Mehrfachauswahl
- 1 Wächter, der jede künftige Entität zur Entscheidung zwingt
- über 3.500 automatische Tests, alle grün
- 4 Commits, ein Branch, kein Zwischenzustand
Was Anwender davon sehen? Wenig — und das ist der Punkt. Listen zeigen nur, was sie zeigen dürfen. Ein Ordner, den ein Kollege seinem Team zuweist, verschwindet samt Inhalt für alle anderen. Eine Website und alle ihre Seiten folgen einer einzigen Zuweisung. Ein Büro mit drei Leuten merkt von alledem nichts. Ein Büro mit dreißig Leuten hat jetzt eine Anwendung, in der Datentrennung nicht Feature ist, sondern Bauform.
Der nächste Schritt liegt damit frei: ein echter Dokument-Öffnen-Dialog mit Suche über alles, was man sehen darf. Und die Poststelle, die immer noch geduldig darauf wartet, zurückschreiben zu lernen.