60 lines
2.5 KiB
PL/PgSQL
60 lines
2.5 KiB
PL/PgSQL
-- DSGVO Art. 17 (Recht auf Vergessenwerden) fuer Empfangs-Audit-Daten.
|
|
-- ====================================================================
|
|
-- Problem: reception_visit_audit ist per Trigger append-only (GoBD).
|
|
-- Bei einem Loeschanspruch nach DSGVO muessen aber personenbezogene
|
|
-- Daten aus dem `payload`-JSON entfernt werden — die strukturelle
|
|
-- Zeile (id, occurred_at, event_kind, actor_kind, actor_id, lead_id)
|
|
-- bleibt fuer den Audit-Trail erhalten.
|
|
--
|
|
-- Loesung:
|
|
-- 1. Spalte `redacted_at` markiert anonymisierte Eintraege.
|
|
-- 2. UPDATE-Trigger erlaubt EINEN spezifischen UPDATE-Pfad: das
|
|
-- Setzen von `redacted_at` UND gleichzeitiges Nullen / Redacten
|
|
-- der payload-PII. Alle anderen UPDATEs werden weiterhin geblockt.
|
|
-- 3. card_leads bekommt auch `redacted_at` — Anonymisierung sowohl
|
|
-- im Hauptdatensatz als auch im Audit-Log.
|
|
|
|
alter table public.reception_visit_audit
|
|
add column if not exists redacted_at timestamptz;
|
|
|
|
alter table public.card_leads
|
|
add column if not exists redacted_at timestamptz;
|
|
|
|
-- Trigger neu definieren: erlaubt UPDATEs die NUR redacted_at + payload
|
|
-- ändern (Anonymisierungs-Pfad). Aenderungen anderer Spalten bleiben
|
|
-- blockiert.
|
|
create or replace function public.reject_reception_audit_modify()
|
|
returns trigger as $$
|
|
begin
|
|
if tg_op = 'DELETE' then
|
|
raise exception 'reception_visit_audit ist append-only — DELETE nicht erlaubt';
|
|
end if;
|
|
-- UPDATE: nur erlauben wenn redacted_at von NULL auf NOT NULL gesetzt
|
|
-- wird UND keine anderen identitaetsrelevanten Felder geaendert werden.
|
|
if tg_op = 'UPDATE' then
|
|
if old.redacted_at is not null then
|
|
raise exception 'Eintrag wurde bereits anonymisiert — keine weiteren UPDATEs';
|
|
end if;
|
|
if new.redacted_at is null then
|
|
raise exception 'reception_visit_audit-UPDATE nur fuer DSGVO-Anonymisierung erlaubt (redacted_at muss gesetzt werden)';
|
|
end if;
|
|
if new.id <> old.id
|
|
or new.lead_id is distinct from old.lead_id
|
|
or new.occurred_at <> old.occurred_at
|
|
or new.event_kind <> old.event_kind
|
|
or new.actor_kind <> old.actor_kind
|
|
or new.actor_id is distinct from old.actor_id
|
|
then
|
|
raise exception 'reception_visit_audit-Anonymisierung darf nur payload + redacted_at aendern';
|
|
end if;
|
|
return new;
|
|
end if;
|
|
return new;
|
|
end;
|
|
$$ language plpgsql;
|
|
|
|
-- Index fuer schnelle Filterung "noch nicht anonymisiert".
|
|
create index if not exists reception_visit_audit_active_idx
|
|
on public.reception_visit_audit (occurred_at desc)
|
|
where redacted_at is null;
|