Commit Graph

2 Commits

Author SHA1 Message Date
520242e3c3 Die Selbstpruefung ruft die Funktion nicht mehr auf
Some checks failed
CI / Migrationen auf leerer Datenbank (push) Has been cancelled
CI / Lint, Typen, Tests, Build (push) Has been cancelled
Die Migration ist an ihrer eigenen Pruefung gescheitert:

  FEHLGESCHLAGEN 20260924220000_reporting_lines_ohne_zeilenschutz.sql
  Nicht berechtigt: nur aktive HR-Benutzer:innen duerfen diese Aktion
  ausfuehren.

Die Pruefung verglich die Zeilenzahl der neuen Funktion mit der Zahl der
laufenden Besetzungen -- und rief sie dafuer auf. Die Funktion verlangt
seit dieser Migration HR-Rechte, die Migration selbst laeuft aber als
Administrator ohne app.user_id. require_hr_admin() hat sie also zu Recht
abgewiesen.

Nichts wurde eingespielt, die Transaktion ist vollstaendig
zurueckgerollt.

Jetzt wird der Koerper gelesen statt ausgefuehrt: geprueft wird, dass
require_hr_admin, security definer und der search_path stehen und dass
die Bestandteile der Abfrage vollzaehlig uebertragen sind. Eine
Selbstpruefung darf nichts aufrufen, was einen angemeldeten
Anwendungsbenutzer voraussetzt.
2026-09-24 12:33:19 +02:00
6d006e55fe om_reporting_lines prueft die Rechte einmal statt je Zeile
Some checks failed
CI / Lint, Typen, Tests, Build (push) Successful in 11m10s
CI / Migrationen auf leerer Datenbank (push) Failing after 5m29s
Die Akte brauchte 1,3 bis 1,5 s, davon ueber 1,1 s in dieser Funktion.
Als postgres -- am Zeilenschutz vorbei -- kostet dieselbe Funktion 60 ms.

  als postgres                             60 ms
  als alpenwerk_app, vor 20260924200000  1457 ms
  als alpenwerk_app, danach              1153 ms

Die vorige Migration fasste die Policies so, dass is_hr_user() je Abfrage
einmal laeuft. Das half um ein Fuenftel -- die Pruefung war also nicht der
Hauptposten.

Die Teile der Funktion sind einzeln billig, unter Zeilenschutz gemessen:
Besetzungen 12 ms, Vorfahrenkette 7 ms, Leitungen 9 ms. Zusammen 28 ms,
die Funktion daraus 1153. Die Kosten entstehen beim Zusammensetzen: unter
Zeilenschutz wird aus jeder Tabellenreferenz eine Unterabfrage mit
Sicherheitsschranke, und der Planer verliert die Freiheit, die
Zwischenmengen einmal zu berechnen -- die beiden Unterabfragen am Ende
laufen dann fuer jede der 784 Zeilen erneut.

Jetzt security definer mit require_hr_admin() in der ersten Zeile. Das ist
dieselbe Bedingung, die in den Policies aller vier gelesenen Tabellen
steht -- require_hr_admin() ist woertlich "if not is_hr_user() then
raise". Sie wird einmal gestellt statt hunderttausendfach. Wer Daten
bekommt, aendert sich nicht; eine unberechtigte Anfrage bekommt jetzt eine
Meldung statt einer leeren Antwort.

Der Preis: diese Funktion liest die vier Tabellen am Zeilenschutz vorbei.
Kaeme je eine feinere Rechteordnung, muesste sie eigens nachgezogen
werden. Mit dem Eigentuemer besprochen -- solche Rollen sind nicht
vorgesehen.

Der Koerper ist unveraendert aus 20260727120100, der einzigen textlichen
Fassung. Geaendert sind Sprache, security definer, search_path und die
erste Zeile.
2026-09-24 12:13:42 +02:00