Zum Inhalt springen

Rechte und Tokens

Wer lesen und schreiben darf, steht in jeder Sammlung unter permissions. Welchen Kopf du mitschickst, entscheidet, welche Rolle du bist. Beispiele stammen aus content.yml, comment.yml, api/hooks/config.js und filter_public.js.

content.yml — öffentlich lesen, Rest für Nutzer und Token
# aus content.yml — öffentlich lesen, Rest für angemeldete Nutzer und Token
permissions:
public:
methods:
get: true
user:
methods:
get: true
post: true
put: true
delete: true
"token:${ADMIN_TOKEN}":
methods:
get: true
post: true
put: true
delete: true
Block Wer ist das Beispiel
public Anonyme Anfragen ohne Kopf content, navigation: get: true
user Angemeldet per JWT (X-Auth-Token) content, comment: volles CRUD
"token:${ADMIN_TOKEN}" Skript mit statischem Token (Token-Kopf) alle Starter-Sammlungen: volles CRUD
  • methods kennt get, post, put, delete.
  • Fehlt ein Block, darf die Rolle nichts.
  • tag.yml hat zusätzlich post/put/delete-Hooks (clear_cache.js), aber keinen Lese-Hook.

Vier Köpfe, vier Zwecke. Verwechsle sie nicht.

Kopf Wert Wofür
Token ADMIN_TOKEN aus api/config.yml.env Sammlungs-CRUD, wenn der Permission-Schlüssel token:${ADMIN_TOKEN} heißt
X-Auth-Token JWT nach Login Nutzer-Aktionen (user:-Rechte)
X-Admin-Token Server-Admin-Token System-Ebene: Projekte, Reload, Server-Ops
Authorization: Bearer … ADMIN_TOKEN Reverse-Proxy-Reload auf LIVE_URL/STAGING_URL (projektlokal, kein Server-API-Kopf)
Lesen ohne Kopf (public.get)
GET /api/v1/_/<namespace>/content?filter={"lang":"de"}
# kein Kopf nötig — public.get ist true

Anonyme Leser bekommen automatisch einen Zusatzfilter. filter_public.js hängt ihn an jede Abfrage ohne Login:

api/hooks/filter_public.js — nur ohne Login aktiv
// api/hooks/filter_public.js — nur ohne Login aktiv
hookResponse.filter = publishedFilter; // bzw. $and mit deinem filter
api/hooks/config.js — der Filter im Kern
// api/hooks/config.js — der Filter im Kern
const publishedFilter = {
active: true,
$or: [
{ publication: { $exists: false } },
{ publication: null },
// … plus Fenster publication.from <= jetzt <= publication.to
],
}

Das heißt: öffentlich sichtbar ist nur active: true innerhalb des Veröffentlichungsfensters.

Sammlung Falle Folge ohne active:true
content Feld active existiert, Hook filtert Seite öffentlich unsichtbar
navigation Feld active existiert nicht im Schema, Hook filtert trotzdem Header/Footer öffentlich leer (count 0), SSR-Cache ohne Navigation
medialib Feld active existiert nicht im Schema, Hook filtert trotzdem Lookups öffentlich unaufgelöst (nur IDs, kein _lookup), Hero zeigt Verlauf, Richtext-Bilder fehlen
tag Kein Lese-Hook Keine Falle — active unnötig
comment Feld active existiert, Hook filtert Nur freigeschaltete Kommentare sind öffentlich

Regel: Lege auf jedem content-, navigation-, medialib- und comment-Dokument active: true ab. Mit Token oder JWT siehst du alles — prüfe Anonymes daher immer ohne Kopf (z. B. Inkognito-Fenster).

Größere Installationen gruppieren Nutzer und Projekte:

  • Org: Mandant oben (matrixorga, matrixorgb im Test).
  • Team: Gruppe in einer Org, mit oder ohne Rechte (z. B. teama1 mit editor).
  • Projekt: hängt an einer Org (orgId) und kennt seine Teams.
  • Org-Admin: Rolle admin in einer Org — darf org-eigene Projekte, aber nichts in fremden Orgs.
Org-, Team- und Projekt-Struktur (Beispiel)
Org A ─┬─ Team A1 (editor) ─┬─ Projekt A
└─ Team A2 (ohne) ─┘
Org B ─── Team B1 (editor) ─── Projekt B

Mitglieder von Team A1 kommen in Projekt A, aber nicht in Projekt B. Ohne Org-Zugehörigkeit geht gar nichts. Details regelt der Server-Code (access_control_matrix_test.go) — für normale Projekte reicht: ein Projekt, ein Team, ADMIN_TOKEN für Skripte.

Persönliche Tokens (POST /api/v1/token, siehe System-Routen) tragen Scopes — Engpässe, die du pro Token vergibst. GET /api/v1/token/_scopes listet, was du aktuell vergeben darfst (gleiche Quelle wie die Anlage-Prüfung).

Gruppe Beispiele Bedeutung
System user.read, project.create, team.delete, org.read, audit.read, llm.chat CRUD je Domäne (user, project, team, org); audit nur read, llm nur chat; Projekt-Admin zusätzlich project.reload, project.backup, project.regenerate-search, project.snapshot
Namespace namespace → alles; namespace.shop → alle Sammlungen dort; namespace.shop.content → alle Aktionen dort; namespace.shop.content.read → genau eine Aktion; namespace.shop._actions.name.post → Aktions-Endpunkt Je länger, desto enger; * matcht genau ein Segment
Besitzer-Kürzel Sammlungs-Kurzform ohne Aktion (z. B. namespace.shop.content) Alle Aktionen, die der Besitzer dort aktuell darf

Die token-Collection-Felder im Überblick (token.go):

Feld Inhalt
userId Besitzer (Nutzer-ID)
name Anzeigename (required, 1–100 Zeichen)
token Klartext — nur in der Anlage-Antwort (tibi_pat_<id>_<secret>), danach nie wieder
tokenHash / tokenPrefix SHA-256-Hash (gespeichert) / Anzeige-Präfix (tibi_pat_ + 4 Zeichen)
scopes String-Liste aus dem Katalog oben
expiresAt Ablaufzeitpunkt; leer = kein Ablauf
lastUsedAt Letzte Nutzung (wird bei Gebrauch aktualisiert)

Skripte mit X-Admin-Token brauchen je Aktion ein Flag im Projekt (tokens[] in der Projekt-Config, Schema project.schema.json):

Flag Erlaubt
allowReload POST …/_/admin/reload (Config neu laden)
allowRegenerateSearch POST …/_/admin/regenerate-search (Suchindex neu aufbauen)
allowBackup Backup herunter- und wiederherstellen
allowSnapshots Snapshots auslösen, listen, löschen, wiederherstellen, herunterladen
Projekt-Config — Token mit Reload- und Backup-Recht
tokens:
- token: ersetze-mich-durch-zufall
allowReload: true
allowBackup: true

Ohne das passende Flag antwortet der Endpunkt mit 403 — das Token ist dann zwar gültig, aber für diese Aktion nicht freigeschaltet. Feld-Rechte pro Rolle (readonlyFields, hiddenFields, validProjections) stehen auf der eigenen Seite Feld-Rechte.

Antwort Ursache Lösung
permission denied Rolle darf die Methode nicht oder Projektion ist fremd permissions-Block und gesendeten Kopf prüfen
project not found Namespace falsch oder Projekt nicht registriert Neues Projekt Schritt 5
lookup permission denied Leserecht auf der Ziel-Sammlung fehlt Ziel-Sammlung braucht public.get oder gleichen Kopf
Öffentlich leer, mit Token voll active:true fehlt oder publication-Fenster liegt in der Zukunft/Vergangenheit active:true setzen, publication.from/publication.to prüfen
Falscher Kopf, richtige Rolle z. B. Token: statt X-Auth-Token: bei Nutzer-Rechten Tabelle oben: Kopf an Rolle anpassen

Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter, Hooks und Permissions), am 68b4278 2026-10-17 (tibi-server, access_control_matrix_test.go, Lookup-/Projektions-Prüfung), am 68b4278 2026-10-17 (tibi-server, Scopes: scope.go, token.go, token_scope_catalog.go; Projekt-Token-Flags: project.go, project.schema.json)