Die Berechtigungs-Engine – Funktionsstamm, Ausführungs- und Anzeigeprüfung

Der Funktionskatalog, der Erkennungslauf, die Entscheidungsreihenfolge der Ausführungs- und Anzeigeprüfung, die Zuweisungstabelle und bekannte Einschränkungen.

Überblick

Die Engine läuft bei jeder Admin-Anfrage: Der Startschritt registriert die Bibliotheken, die Controller rufen die Methoden von dlbtpermission auf. Drei Ebenen: 1) Basis-Routenrecht (Zugriff, Ändern), 2) Funktionsberechtigung, 3) Inhaltsbereich. Die Tabellennamen tragen in der Datenbank das eigene Präfix der Installation.

Funktionsstamm (feature)

Der einzige Funktionsstamm: Eine Zeile ist ein Paar aus route und action_key. Hauptfelder: module, name, access_type (UNMAPPED, BASE, SUBSCRIPTION, SYSTEM; Standard UNMAPPED), visibility_rule (auto, never, always), parent_feature_id, list_price, currency, uom, usage_mode, limit_*, billing_period, permission_present, gate_covered, cron_related, cron_job_type, status.

Herkunft der Zeilen:

  1. Seed-Migrationen mit ausdrücklicher Einstufung.
  2. Projektion aus dem alten Katalog permission nach jeder Migration (projectPermissionCatalogue(), INSERT IGNORE, UNMAPPED); alte Gruppenzuweisungen werden Gruppen-Vorlagenzeilen mit Quelle role. Rückwärts wird nichts kopiert.
  3. Erkennung: Die Schaltfläche „scan“ des Funktionsstamms (masterdata/feature::scan) ruft Dlbtfeature::scan() auf.

Die Seeds enthalten 523 Paare route::Schlüssel: BASE 45, SUBSCRIPTION 6, SYSTEM 44, UNMAPPED 78, weitere 350 nur im alten Katalog (nach der Projektion UNMAPPED); 122 im Code geprüfte Schlüssel stehen in keinem Seed. Dlbtfeature::classify() lädt den aktiven Stamm einmal je Anfrage; bei null geladenen Zeilen fällt die Prüfung auf den alten Weg zurück.

Erkennung (Dlbtfeature::scan())

  1. Liest nur die Admin-Controllerdateien; je Datei zählt die erste Routendefinition.
  2. Sammelt Aufrufe von canAction( und canActionForGroup(; bei einer Route in einer Variablen wird die eigene Route der Datei verwendet; der Schlüssel muss ein Literal sein.
  3. Erkennt nicht: canSave(), canBulkAction(), canBulk(), canSee(), uiMode(), lockReason(), canActionForUser(), canSeeDeleted(), canStorageContext().
  4. Ergänzt die aktiven Zeilen des alten Katalogs (Vereinigung) und baut aus Dlbtcronhealth::JOBS eine Zuordnung Route → Hintergrundaufgabe.
  5. Bestehende Zeilen erhalten nur die gemessenen Fakten (access_type, Name, Notiz bleiben); neue Zeilen sind UNMAPPED, Status 1.
  6. Liefert added, updated, gap_permission, gap_gate; danach flush().

Ausführungsprüfung (canAction)

  1. Kein angemeldeter Benutzer → verweigert; user_group_id = 1 → erlaubt.
  2. Bei geladenem Stamm: Schlüssel nicht im Stamm → verweigert; SYSTEM → für jeden angemeldeten Benutzer erlaubt.
  3. Sonst ist eine HEUTE gültige Zeile mit access_mode = 'usable' in user_feature nötig (beliebige Quelle; beide Datumsenden zählen, NULL ist offen). Fehlt sie, entscheidet die Abhängigkeitsregel.
  4. Mit Zeile → Dlbtentitlement::allows(): UNMAPPED und BASE erlaubt; SUBSCRIPTION erlaubt außer bei überfälliger Schuld (nur bei eingeschalteter Schuldsperre, mit Karenztagen; gemessen an den überfälligen offenen Rechnungen des Mandanten des Benutzers).
  5. Bei leerem oder unlesbarem Stamm entscheidet der alte gruppenbezogene Katalog, danach kann allows() einschränken.

Abhängigkeitsöffnung (dependencyAllows): nur für Leseschlüssel (list, autocomplete, search, select, view, read), wenn der Benutzer eine nutzbare Zeile für eine andere Funktion hat, deren systemseitige Abhängigkeit in feature_dependency hierher zeigt. Eine kommerzielle Abhängigkeit öffnet nichts. Beispiel: Das Erfassen einer Rechnung öffnet die Listen der Partner, Unternehmensgruppen, Rechnungsblöcke und Bankkonten.

Anzeigeprüfung und Oberflächenmodus

  • canSee: Super-Admin, leerer Stamm, Schlüssel nicht im Stamm, visibility_rule = always und SYSTEM → sichtbar; never → verborgen. Sonst ist eine heute gültige Zeile (sichtbar oder nutzbar) nötig; list ist auch sichtbar, wenn eine Zeile für irgendeine Funktion der Route existiert. Bei anderen Schlüsseln müssen auch die Liste der Route und der Schlüssel menu der Menügruppe sichtbar sein (Permission::MENU_GROUPS).
  • uiMode: canAction → ok; sonst canSee → upsell; sonst none.
  • lockReason: Funktion im Stamm, nicht SYSTEM, ohne nutzbare Zeile → subscription; Schuld → debt. Das upsellGuard() des Startschritts leitet vollständige Seitenaufrufe danach auf die Angebotsseite.
  • canSave($route, $id): id > 0 → edit; id = 0 → add, sofern im Stamm, sonst edit. canBulk, canBulkAction: Der Schlüssel mit Präfix bulk entscheidet, falls vorhanden. canSeeDeleted: Super-Admin oder delete. getAllowedScopeValues: Inhaltsregeln (die eigene Regel des Benutzers geht der Gruppenregel vor; nur Erlaubniswerte).

Zuweisung (user_feature)

Die einzige Zuweisungstabelle: user_id (bei Gruppenvorlage user_group_id), feature_id, date_from, date_to, source (role, subscription, order, manual), source_ref, origin, access_mode (visible, usable), offer_definition_id, is_override, Limit- und Preisfelder, definition_id, plan_id. Eine Gruppenzeile (user_id = 0, source = 'role') ist eine Vorlage, die die Prüfung nicht liest; syncGroupRoleRows() und fillUserRoleFromGroup() zerlegen sie in Benutzerzeilen. Schreibwege: Dlbtentitlement::grant() und grantStructure(), Dlbtprovision::materialise(), saveUserFeatureRows() und addMeteredOrder() des Funktionsberechtigungs-Modells, Dlbtpayment::syncGrants(), Dlbtsignup::creditOnly(), materialiseLimits() des Abonnement-Modells.

Bekannte Einschränkungen

  • Die Spalte source zählt in der Prüfung nicht: PAID_SOURCES und hasPaidFeature() werden nirgends aufgerufen.
  • Weder canSee noch canAction nutzt die Hierarchie parent_feature_id.
  • MENU_GROUPS kennt zum Beispiel die Routen von Bestellkatalog, Bestellungen, Zusatzpaketen, Paketfamilien, Registrierung, Test-Bootstrap und Kasse nicht; dort entfällt die Menügruppen-Bedingung.
  • isVisibleGrant(), das die alte Spalte visible liest, wird nie aufgerufen.