Selbstauskunft-Endpunkt /api/me - #56
Merged
Merged
Conversation
Clients und Agenten konnten bisher nicht ermitteln, welche Endpunkte ein Token nutzen darf: Die OpenAPI-Spec lag nur hinter der Backend-Seite (page=api/openapi, perm api[]), und ein Token hatte keine Selbstauskunft über seine Scopes. Die Pfade mussten damit außerhalb der API mitgeteilt werden, weil sie sich nicht zuverlässig aus dem Scope-Namen ableiten lassen. - GET /api/me listet ausschließlich die Endpunkte, deren Scope vorhanden ist, mit Methoden, Pfad, Beschreibung und Parametern (path/query/body inkl. Typ, required, Default, Beschreibung). Default-Format kompakt, ?format=openapi liefert dieselbe Menge als gefilterte OpenAPI-3.0-Spec über den bestehenden OpenAPIConfig-Generator. - Der Endpunkt braucht keinen eigenen Scope: Auth::requiresScope() und new BearerAuth(false) autorisieren jedes gültige Token, sonst fehlte die Auskunft genau bei den Tokens, bei denen der Scope vergessen wurde. Token::getAvailableScopes() filtert solche Routen aus der Token-Seite. - Backend-Spiegel GET /api/backend/me für Session-Zugriffe. Dort wird nicht vorab gefiltert, da Backend-Permissions pro Request geprüft werden; der Hinweis steht in meta.note. - RouteCollection::handle() klont die Routen vor dem Präfixen, damit die registrierten Route-Objekte ihren unpräfixierten Pfad behalten und Controller, die getRoutes() auslesen, /api nicht doppelt sehen. - Bei gültigem Token ohne passenden Scope nennt die 401-Antwort den fehlenden Scope (required_scope); bei ungültigem Token bleibt das Feld weg. Statuscode unverändert. Refs #55
Die Spezifikation wird durch /api/me?format=openapi jetzt maschinenlesbar ausgeliefert und nicht mehr nur von Swagger UI gerendert. Dabei fielen zwei Punkte auf, die getypte Parser stolpern lassen: - "tags" war ein nach Tag-Namen indiziertes Objekt statt eines Arrays und damit kein gültiges OpenAPI-Dokument (array_values). - Fehlt für einen Tag der Sprachschlüssel, stand "[translate:...]" als Beschreibung in der Ausgabe. Tags können von fremden AddOns kommen, deren Schlüssel das api-AddOn nicht kennt — ohne Übersetzung bleibt die Beschreibung jetzt leer. Zusätzlich die Sprachschlüssel für die eigenen Tags "default" und "backend" ergänzt und ein Test, der beides für /api/me?format=openapi absichert.
OpenAPI verlangt Typ und Default eines Parameters innerhalb von "schema"; bisher stand "default" direkt am Parameter und "schema" fehlte bei allen einfachen Query-Parametern ganz. Die Typen der Route-Definitionen (int, bool, float) werden dabei auf die OpenAPI-Namen gemappt, ein Default von null wird weggelassen, weil er dem deklarierten Typ widerspricht. Bei den deepObject-Parametern (filter[...]) trugen die Properties "required" als Bool — dort gehört es als Liste auf Objektebene. Außerdem kamen "description" und "required" des Objekt-Parameters selbst aus der letzten Iteration der Feld-Schleife, also vom letzten Filterfeld. Test prüft für /api/me?format=openapi, dass jeder Parameter ein schema mit gültigem Typ hat und kein default auf Parameter-Ebene trägt.
Member
Author
|
Nachtrag nach ausführlichem Variantentest gegen eine laufende Instanz (Token ohne Scopes, deaktiviertes Token, verwaister Scope, falsche Methode, Trailing-Slash, doppelte Query-Parameter, Backend-Session als Admin und als eingeschränkter User). Verhalten überall wie erwartet; dabei sind drei Fehler in
Alles drei behoben und mit Tests abgedeckt. Fremde AddOn-Routen erscheinen übrigens automatisch in Suite: 186 Tests / 1806 Assertions grün. Dieser Text wurde durch eine KI erstellt. |
- $requireScope war eine promoted Constructor-Property und damit auf Klassenebene ohne Default. Erweitert ein anderes AddOn BearerAuth und überschreibt __construct() ohne parent::__construct(), lief requiresScope() in "Typed property must not be accessed before initialization". Property jetzt klassisch mit Default deklariert. - Die Parameter-Beschreibung stand doppelt in der Spec: am Parameter und im schema. Im schema bleibt sie nur für Objekt-Properties, die keine Parameter-Ebene haben.
Beschreibungen können sehr lang werden — die Routen des ai_platform-AddOns liegen bei bis zu 2095 Zeichen und schieben die Zeile der Endpunktliste mehrzeilig auseinander. In der Zeile stehen jetzt 50 Zeichen, der volle Text sitzt im title-Attribut und erscheint beim Hover. Nur Darstellung: die ausgelieferte Spezifikation bleibt unverändert, damit Clients die vollständige Beschreibung weiter bekommen. Ein MutationObserver fängt das Neu-Rendern beim Auf- und Zuklappen ab; das title-Attribut dient dabei als Quelle des Originaltexts, nicht ein Merker-Flag.
…Entwickler README: die Kürzung der Beschreibungen in der Endpunktliste (Anzeige, nicht Spec), und was AddOn-Entwickler von der Selbstauskunft haben — eigene Routen erscheinen automatisch, ausgegeben wird aber nur, was die Route deklariert; Datei-Uploads brauchen 'type' => 'file', eigene Tags einen Sprachschlüssel. CLAUDE.md: Konventionen des OpenAPI-Generators (Typmapping über getSchema, tags als Array, Tag-Beschreibung ohne Sprachschlüssel leer) inklusive der bekannten Lücke im Body-Zweig, plus der Hintergrund zur Swagger-UI-Kürzung.
Der Body-Zweig übernahm den Typ wörtlich aus der Route-Definition, dort stehen aber die Typnamen von rex_type::cast(): "int" ist kein OpenAPI-Typ, Client-Generatoren verlieren damit die Typinformation. Betroffen waren 13 Properties in Structure und Metainfo. Zwei weitere Verstöße an derselben Stelle behoben: - "required" stand als Bool in der Property; die Liste auf Objektebene wird ohnehin korrekt gefüllt, der Bool war eine Doppelung in falscher Form (406 Properties). - Hat eine Route kein Pflichtfeld, entstand "required": [] — JSON Schema verlangt mindestens ein Element, der Schlüssel bleibt jetzt weg (21 Operationen). Der file-Sonderfall bleibt: 'type' => 'file' ergänzt format: binary und schaltet den Content-Type auf multipart/form-data. Als Nebeneffekt stehen Defaults jetzt auch im Body-Schema — Swagger UI zeigt für Slices "module_id": 0 und "ctype_id": 1 statt "string".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Löst #55:
GET /api/mesagt dem Aufrufer, was er darf — gelistet werden nur Endpunkte mit vorhandenem Scope, mit Pfad, Methoden, Beschreibung und Parametern (path/query/body inkl. Typ,required, Default). Default ist das kompakte Format,?format=openapiliefert dieselbe Menge als gefilterte OpenAPI-3.0-Spec über den bestehenden Generator.GET /api/backend/me; dort ohne Vorfilterung, weil Permissions pro Request geprüft werden (Hinweis inmeta.note).required_scope— Statuscode unverändert.handle()klont Routen vor dem Präfixen, damitgetRoutes()-Leser/apinicht doppelt sehen.Response-Schemas pro Route (Listen- vs. Detailform) sind bewusst nicht Teil des PRs.
Tests: 184 Tests / 1418 Assertions grün gegen eine lokale Instanz, inkl. 10 neuer Bearer- und 3 neuer Backend-Tests.
Dieser Text wurde durch eine KI erstellt.