enhancement
major
minor
major
minor
minor
#29675
TL Views: Anlegen und Löschen richten sich nach den Modell-Zugriffsrechten, statt erst nach dem Ausfüllen mit „Kopieren nicht erlaubt“ abgelehnt zu werden
Problem
Die View-Schicht (.view.xml) legt Objekte an, indem sie ein transientes Objekt in einem Dialog bearbeitet und es mit $obj.copy(transient: false) persistiert. Ein Benutzer ohne das Recht, den Typ anzulegen, füllt den ganzen Dialog aus, klickt auf "Erstellen" und erhält:
"Kopieren nicht erlaubt: Das Erzeugen einer Instanz vom Typ "Projekt" ist nicht gestattet."
Der Benutzer hat nichts kopiert, der Präfix "Kopieren nicht erlaubt" ist für ihn sinnlos. Gesehen in der TL-Development-App, "Neues Projekt" als angemeldeter Benutzer ohne Rollen, Snapshot-Build 42.
Allgemeiner: Die Modell-Zugriffsrechte (SecurityConfigurationService, abgefragt über ModelAccessRights) erreichen die View-Schicht kaum. TL-Script filtert Ergebnisse nach dem Leserecht, und set/`new`/`copy`/`delete` werfen bei fehlendem Recht einen Fehler. Befehle zum Anlegen und Löschen werden aber immer angeboten, und die Ablehnung kommt erst, wenn der Benutzer die Aktion ausführt. Das Speichern eines Formulars (TLObjectOverlay.apply) prüft das Schreibrecht überhaupt nicht, und Auswahllisten in Formularen werden nicht nach dem Leserecht gefiltert.
Konzept
Das Modell entscheidet, die View folgt (beschrieben in com.top_logic.layout.view/docs/specs/29108-react-access-control.md, Abschnitt 7):
- Eine Quelle der Wahrheit. Die View-Schicht vergibt keine eigenen Rechte. Eine View-Operation ist eine Modell-Operation auf einem Objekt, Typ oder Attribut. Die Modell-Operationen setzen die Rechte durch; die Oberfläche bietet nicht an, was ohnehin abgelehnt würde.
- Ableiten statt konfigurieren. Wo ein Element Objekt und Attribut kennt (Formularfeld, Tabellenzelle, Bearbeiten eines Formulars), wendet es die Rechte selbst an – Ticket #29692.
- Befehle sagen, was sie tun, nicht welches Recht sie brauchen. Eine Aktion, die weiß, was sie tut (Objekt löschen, Objekt anlegen), bringt die passende Ausführbarkeitsregel selbst mit. Für frei geschriebene Skripte und fachliche Operationen (z.B. "Freigeben") gibt es eine allgemeine Regel.
- Eine zentrale Darstellungsregel. Hängt die Ablehnung an keinem konkreten Objekt (Prüfung gegen die Sicherheitswurzel, z.B. Anlegen auf oberster Ebene; der Typ gewährt die Operation keiner Rolle; eingeschränkter Benutzer), wird der Befehl ausgeblendet. Scheitert die Prüfung an einem konkreten Objekt (Eingabe des Befehls, Container), wird er deaktiviert und nennt den Grund. Eine Regel kann das mit denied="hide|disable" übersteuern.
- Meldungen benennen die Operation.
Lösung
Durchsetzung und Meldungen
- copy(transient: false) eines transienten Objekts meldet eine verweigerte Anlage mit "Anlegen nicht erlaubt: Objekte vom Typ „{0}“ dürfen nicht angelegt werden."; das Kopieren eines persistenten Objekts behält "Kopieren nicht erlaubt".
- Das Speichern eines Formulars prüft das Schreibrecht für jedes geänderte Attribut (TLObjectOverlay.checkApply, dieselbe Prüfung wie TL-Script set). Alle beteiligten Overlays (Formular und Zeilen von Kompositionstabellen) werden geprüft, bevor eines geschrieben wird, so dass eine Ablehnung nichts teilweise speichert. Neue Zeilen einer Kompositionstabelle erfordern das Anlegerecht im Container.
- Auswahllisten von Formularfeldern werden nach dem Leserecht gefiltert, wie die Zeilen einer Tabelle.
Befehle
- Eine Aktion (ViewAction.getIntrinsicRule()) kann eine eigene Ausführbarkeitsregel mitbringen; generic-command, upload-command und with-transaction kombinieren die Regeln ihrer Aktionen mit den konfigurierten.
- Allgemeine Regel für <executability>: <model-access operation="Read|Write|Create|Delete|<Befehlsgruppe>" [object="kanal"] [attribute="name"] [type="modul:Typ"] [container="kanal" reference="name"] [denied="hide|disable"]/>.
- Aktionen mit eingebauter Rechteprüfung:
- <create-transient type="..." [container="..." reference="..."]/> erzeugt den Entwurf für einen Anlege-Dialog; der Befehl wird ohne Anlegerecht nicht angeboten, bevor der Dialog öffnet.
- <persist-transient [type="..."] [container="..." reference="..."]/> persistiert den Entwurf wie copy(transient: false) und hängt ihn optional in die Komposition des Containers ein.
- <delete-object/> löscht die Eingabe wie delete().
- Ein deaktivierter Button zeigt seinen Grund beim Überfahren mit der Maus (bisher verhinderte pointer-events: none den Tooltip).
Demo
- Die Anlege- und Lösch-Befehle der React-Demo verwenden diese Aktionen bzw. <model-access>.
- Die Demo gewährt der Gruppe users die Rolle demo.react.ProjectMember: Projekte lesen und schreiben, Meilensteine und Notizen anlegen, ändern und löschen, Tickets lesen – aber keine Projekte anlegen oder löschen. Ein solcher Benutzer sieht "Neues Projekt" nicht, "Löschen" eines Projekts ist deaktiviert mit dem Grund "Sie dürfen dieses Objekt nicht löschen."
Nicht Teil dieses Tickets: Sichtbarkeit und Bearbeitbarkeit von Formularfeldern und Tabellenzellen sowie das Bearbeiten eines Formulars aus den Rechten ableiten (#29692).