Zeilenschutz auf vier Tabellen nachziehen, und den Zustand pruefen
Beim Neuaufbau der Datenbank aus den Migrationen am 26.08. blieben cost_centers, position_cost_centers, onboarding_tasks und offboarding_tasks ohne Row Level Security: sie verlassen sich auf den Ereignis-Trigger ensure_rls, den eine spaetere Migration erst anlegt. Ein Ereignis-Trigger wirkt nur nach vorne. Die Policies auf diesen Tabellen existieren und wurden nie ausgewertet — PostgreSQL befragt sie nur bei eingeschaltetem RLS. In jeder Aufstellung der Policies sah es richtig aus. Der CI-Job prueft ab jetzt den Zustand nach dem Lauf, nicht nur dass die Dateien durchlaufen. Genau in diesem Zwischenraum ist der Fehler durchgekommen.
This commit is contained in:
71
.github/workflows/ci.yml
vendored
71
.github/workflows/ci.yml
vendored
@@ -117,6 +117,77 @@ jobs:
|
||||
echo "$ausgabe" | grep -q "^Nichts anzuwenden" \
|
||||
|| { echo "Der zweite Lauf wollte erneut anwenden — die Buchführung greift nicht."; exit 1; }
|
||||
|
||||
# Dass die Migrationen durchlaufen, heisst nicht, dass das Ergebnis
|
||||
# stimmt. Bis hierher prüft dieser Job nur, dass keine Datei abbricht —
|
||||
# und genau daran ist ein Fehler vorbeigekommen: vier Tabellen aus dem
|
||||
# August (Kostenstellen, On-/Offboarding) standen ohne Zeilenschutz da,
|
||||
# weil sie sich auf den Ereignis-Trigger `ensure_rls` verliessen, den
|
||||
# eine spätere Migration erst anlegt. Alle Dateien liefen sauber durch,
|
||||
# die Policies standen im Katalog, und ausgewertet wurde keine davon.
|
||||
#
|
||||
# Deshalb wird ab hier der **Zustand** geprüft, nicht der Durchlauf.
|
||||
# Nachgezogen hat das 20260908100000; dort steht dieselbe Gegenprobe.
|
||||
# Sie läuft aber nur einmal, beim Anwenden jener Datei. Der Schritt hier
|
||||
# läuft bei jedem Push gegen eine frisch aufgebaute Datenbank und fängt
|
||||
# deshalb auch die *nächste* Tabelle, die es wieder vergisst.
|
||||
- name: Zugriffsschutz nach dem Lauf
|
||||
env:
|
||||
PGPASSWORD: ci
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
frage() {
|
||||
psql -v ON_ERROR_STOP=1 -h postgres -U postgres -d alpenwerk -At -c "$1"
|
||||
}
|
||||
|
||||
# relkind in ('r','p'): gewöhnliche und partitionierte Tabellen.
|
||||
ohne_rls=$(frage "
|
||||
select coalesce(string_agg(c.relname, ', ' order by c.relname), '')
|
||||
from pg_class c
|
||||
join pg_namespace n on n.oid = c.relnamespace
|
||||
where n.nspname = 'public'
|
||||
and c.relkind in ('r', 'p')
|
||||
and not c.relrowsecurity")
|
||||
|
||||
# RLS ohne Policy ist nicht offen, sondern leer — kein Leck, aber
|
||||
# eine Tabelle, aus der nie etwas zurückkommt.
|
||||
ohne_policy=$(frage "
|
||||
select coalesce(string_agg(c.relname, ', ' order by c.relname), '')
|
||||
from pg_class c
|
||||
join pg_namespace n on n.oid = c.relnamespace
|
||||
where n.nspname = 'public'
|
||||
and c.relkind in ('r', 'p')
|
||||
and not exists (select 1 from pg_policy p where p.polrelid = c.oid)")
|
||||
|
||||
# Das Netz selbst: fehlt oder schläft es, ist die nächste neue
|
||||
# Tabelle wieder ungeschützt.
|
||||
netz=$(frage "
|
||||
select count(*) from pg_event_trigger
|
||||
where evtname = 'ensure_rls' and evtenabled <> 'D'")
|
||||
|
||||
fehler=0
|
||||
if [ -n "$ohne_rls" ]; then
|
||||
echo "Ohne Row Level Security: $ohne_rls"
|
||||
fehler=1
|
||||
fi
|
||||
if [ -n "$ohne_policy" ]; then
|
||||
echo "Mit Row Level Security, aber ohne jede Policy: $ohne_policy"
|
||||
fehler=1
|
||||
fi
|
||||
if [ "$netz" != "1" ]; then
|
||||
echo "Der Ereignis-Trigger ensure_rls fehlt oder ist abgeschaltet."
|
||||
fehler=1
|
||||
fi
|
||||
|
||||
if [ "$fehler" -ne 0 ]; then
|
||||
echo
|
||||
echo "Jede Tabelle in public braucht RLS und mindestens eine Policy."
|
||||
echo "Nachziehen in einer neuen Migration, nicht in einer bereits angewendeten."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Zugriffsschutz vollstaendig: jede Tabelle mit RLS und Policy, ensure_rls aktiv."
|
||||
|
||||
# Die Integrationstests brauchen einen befüllten Bestand; der Seed lag
|
||||
# in der abgelösten Umgebung und ist noch nicht nachgezogen (siehe
|
||||
# README, offene Punkte). Bis dahin laufen sie gegen eine erreichbare
|
||||
|
||||
Reference in New Issue
Block a user