-- ============================================================================= -- Phase 4: Mandantentrennung und Regeln für den Terminalbetrieb. -- -- Drei Dinge: -- -- 1. Row Level Security für die sieben neuen Tabellen. -- -- 2. Regeln, die verhindern, dass ein offline erfasster Vorgang beim -- Nachtragen Unsinn anrichtet: kein Kilometerstand, der rückwärts läuft, -- keine Rückkehr vor der Abfahrt, keine Anwesenheit, die vor dem Kommen -- endet. -- -- 3. Indizes für die Abfragen, die das Terminal und der Hallenmonitor -- dauernd stellen: wer ist gerade da, welches Fahrzeug ist unterwegs. -- ============================================================================= -- ----------------------------------------------------------------------------- -- Row Level Security -- ----------------------------------------------------------------------------- DO $$ DECLARE tabelle text; BEGIN FOREACH tabelle IN ARRAY ARRAY[ 'fahrt', 'anwesenheit', 'waescheabgabe', 'inventurgang', 'inventurposten', 'kiosk_vorgang', 'sync_konflikt' ] LOOP EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', tabelle); EXECUTE format( 'CREATE POLICY %I ON %I FOR ALL ' 'USING ("organisationId" = aktuelle_organisation()) ' 'WITH CHECK ("organisationId" = aktuelle_organisation())', tabelle || '_mandant', tabelle ); END LOOP; END; $$; -- ----------------------------------------------------------------------------- -- Was am Terminal nicht passieren darf -- -- Diese Bedingungen fangen ab, was beim Eintippen auf einem Tablet mit -- Handschuhen tatsächlich passiert: eine vertippte Ziffer im Kilometerstand, -- ein Rückkehrzeitpunkt vor der Abfahrt. Im Gerätehaus schaut niemand ein -- zweites Mal hin, und offline erfasste Vorgänge kommen unter Umständen erst -- Stunden später an. -- ----------------------------------------------------------------------------- ALTER TABLE "fahrt" ADD CONSTRAINT "fahrt_kilometer_vorwaerts" CHECK ( "kilometerRueckkehr" IS NULL OR "kilometerRueckkehr" >= "kilometerAbfahrt" ); ALTER TABLE "fahrt" ADD CONSTRAINT "fahrt_rueckkehr_nach_abfahrt" CHECK ( "rueckkehrUm" IS NULL OR "rueckkehrUm" >= "abfahrtUm" ); ALTER TABLE "anwesenheit" ADD CONSTRAINT "anwesenheit_gehen_nach_kommen" CHECK ( "abgemeldetUm" IS NULL OR "abgemeldetUm" >= "angemeldetUm" ); ALTER TABLE "waescheabgabe" ADD CONSTRAINT "waescheabgabe_menge_positiv" CHECK ( "menge" > 0 ); -- ----------------------------------------------------------------------------- -- Indizes für den laufenden Betrieb -- -- Der Hallenmonitor fragt im Sekundentakt dasselbe: wer ist da, welches -- Fahrzeug ist draußen. Beides sind Teilmengen, die klein bleiben, während -- die Tabellen über Jahre wachsen — also Teilindizes. -- ----------------------------------------------------------------------------- CREATE INDEX "anwesenheit_anwesend" ON "anwesenheit" ("organisationId", "angemeldetUm" DESC) WHERE "abgemeldetUm" IS NULL; CREATE INDEX "fahrt_unterwegs" ON "fahrt" ("organisationId", "fahrzeugId", "abfahrtUm" DESC) WHERE "rueckkehrUm" IS NULL; CREATE INDEX "waesche_offen" ON "waescheabgabe" ("organisationId", "personId") WHERE "status" <> 'ZURUECK'; CREATE INDEX "sync_konflikt_offen" ON "sync_konflikt" ("organisationId", "entstandenAm" DESC) WHERE "erledigtAm" IS NULL; -- ----------------------------------------------------------------------------- -- Rechte für die Anwendungsrolle auf die neuen Tabellen. -- ----------------------------------------------------------------------------- DO $$ BEGIN IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'florian_app') THEN EXECUTE 'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO florian_app'; EXECUTE 'GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO florian_app'; EXECUTE 'REVOKE INSERT, UPDATE, DELETE ON "berechtigung" FROM florian_app'; EXECUTE 'REVOKE UPDATE, DELETE ON "audit_log" FROM florian_app'; -- Ein empfangener Terminalvorgang wird nie geaendert oder geloescht: -- er ist der Beleg dafuer, dass etwas schon einmal angekommen ist. -- Ohne diese Sperre koennte ein zweiter Sendeversuch den ersten -- ueberschreiben und derselbe Vorgang doch zweimal wirken. EXECUTE 'REVOKE UPDATE, DELETE ON "kiosk_vorgang" FROM florian_app'; ELSE RAISE NOTICE 'Rolle florian_app nicht vorhanden — Rechtevergabe uebersprungen.'; END IF; END; $$;