Skip to content

ADR-0051: Sysadmin legt Organisationen an; Control-DB nur Metadaten

  • Status: accepted
  • Datum: 2026-09-02
  • Phase: Multi-Tenant
  • Bezug: ADR-0031, ADR-0032
  • Ändert: ADR-0032 (Selbstanlage, impliziter Plattform-Operator)

Kontext

ADR-0032 band den Konto-Lebenszyklus an Selbstanlage: ein User ohne tenant_id legt per POST /tenants eine Organisation an und wird Org-Admin. Der Plattform-Operator war nur role=admin plus tenant_id null. Lokal setzte LINETRA_SHARED_DB=true tenants.dbname auf die Control-DB linetra, sodass GIS, Projekte und föderierte Entities neben app_users landeten.

Gewollt ist: ein Bootstrap-Sysadmin (Env) legt Organisationen an und bestimmt Org-Admins; Org-Admins verwalten nur die eigene Orga; die Control-DB hält nur Web-Metadaten.

Entscheidung

  1. app_users.is_system_admin ist das Plattform-Flag. Org-Rolle bleibt role (admin / editor / reader plus Fachrollen).
  2. Bootstrap: leere app_users → ein Sysadmin aus LINETRA_BOOTSTRAP_ADMIN_*. Leerer Tenant-Katalog (oder eine Shared-dev-Zeile) → Organisation Nasarek Data Engineering, Slug nasarek, DB linetra_nasarek. Der Bootstrap-User ist Sysadmin und Org-Admin; Person Robert Nasarek lebt in der Org-DB.
  3. POST /tenants nur Sysadmin. Body enthält den ersten Org-Admin (Username, Passwort, Vorname, Nachname, E-Mail). Keine Selbstanlage.
  4. Weitere Sysadmins legt nur ein Sysadmin an (POST /users mit is_system_admin). Der letzte aktive Sysadmin bleibt unantastbar.
  5. Mitgliedschaft bleibt 1:1 (app_users.tenant_id). Sysadmins dürfen tenant_id null haben und per Session / X-Tenant-Id eine Orga wählen.
  6. Control-DB linetra: tenants, app_users, editor_access_tokens, client_cert_grants. Fachdaten (Projekte, GIS, Schema-CMS, föderierte Records, Akteure) nur in linetra_<slug>.
  7. Kein stilles Shared-Fallback außerhalb von Tests: tenant.dbname == POSTGRES_DB ist ein Fehler, wenn LINETRA_SHARED_DB aus ist. Pytest behält Shared-DB.

Begründung (für dieses Setup)

Zwei Ebenen (Plattform / Organisation) reichen: Isolationseinheit bleibt die Orga-DB (ADR-0030/0031). Ein explizites Sysadmin-Flag verhindert, dass jeder Org-Admin Plattformrechte erbt. Personfelder am Org-Admin gehören in persons der Tenant-DB, nicht auf app_users. Die bestehende Tenant-Session (get_db_session) schreibt Fachdaten bereits gegen tenants.dbname — die Lücke war Shared-DB, nicht die Route.

Verworfene Alternativen

Alternative Warum verworfen
Selbstanlage (ADR-0032 Punkt 2) Orgs entstehen durch Sysadmins, nicht durch den ersten Login
role=system_admin statt Flag Bootstrap-User ist Sysadmin und Org-Admin derselben Orga
M:N User ↔ Org Ein Mitarbeiter gehört einer Orga
Drei Ebenen Tenant → Orgs Organisation = Tenant = eine DB
Casbin / Oso Vier klare Stufen, Principal existiert schon
RLS / Schema-pro-Tenant Isolation war ADR-0030/0031
Namensfelder auf app_users Vorname/E-Mail sind Akteur (persons)
Control-Alembic-Geschichte umschreiben Fach-DDL bleibt historisch; neue Fach-DDL nur unter alembic/tenant/

Folgen

  • ADR-0032 Punkte 2 und 7 gelten nicht mehr. Föderierter Stamm und GIS-project_id bleiben.
  • Principal: can_manage_tenants / can_view_platform_settings nur Sysadmin; can_manage_users für Org-Admin oder Sysadmin; can_create_project für Org-Admin oder Sysadmin im Org-Kontext.
  • Settings → System (Sysadmin): weitere Sysadmins, Org-Katalog. Settings → Organisation: kein Selbst-Create.
  • QGIS /auth/me.qgis_connection.database ist linetra_<slug>, nicht linetra.
  • Optionaler Ops-Schritt nach dem Cutover: scripts/strip_control_fachdaten.sh (Keep-Liste inkl. client_cert_grants).
  • Teardown (Sysadmin): GET /tenants/{id}/backup liefert ZIP (pg_dump + Control-Metadaten + Projektdateien) und Header X-Deletion-Token. DELETE /tenants/{id} mit confirm_slug + Token droppt Control-Zeilen, linetra_<slug>, Rollen und Dateiblobs. Bootstrap-Org und LINETRA_SHARED_DB=true sind nicht löschbar. Restore bleibt manuell aus dem ZIP.