Files
accounted/supabase/migrations/20240101000014_audit_log.sql
T
Jakob Wennberg cdf1dcc4c8 New Base func
2026-02-19 09:48:02 +01:00

60 lines
2.2 KiB
PL/PgSQL

-- Migration 14: Audit Log
-- Append-only audit log for all compliance-critical mutations
-- =============================================================================
-- 1. audit_log table
-- =============================================================================
CREATE TABLE public.audit_log (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id uuid NOT NULL, -- No FK cascade: survives user deletion
action text NOT NULL CHECK (action IN (
'INSERT', 'UPDATE', 'DELETE',
'COMMIT', 'REVERSE', 'CORRECT',
'LOCK_PERIOD', 'CLOSE_PERIOD',
'DOCUMENT_DELETE_BLOCKED', 'RETENTION_BLOCK',
'SECURITY_EVENT'
)),
table_name text,
record_id uuid,
actor_id uuid,
old_state jsonb,
new_state jsonb,
description text,
created_at timestamptz NOT NULL DEFAULT now()
-- Intentionally NO updated_at: append-only
);
ALTER TABLE public.audit_log ENABLE ROW LEVEL SECURITY;
-- Users can only read their own audit log entries
CREATE POLICY "audit_log_select" ON public.audit_log
FOR SELECT USING (auth.uid() = user_id);
-- No INSERT policy for normal users -- audit log is written by SECURITY DEFINER triggers
-- No UPDATE or DELETE policies -- immutability enforced by triggers below
CREATE INDEX idx_audit_log_user_id ON public.audit_log (user_id);
CREATE INDEX idx_audit_log_table_record ON public.audit_log (table_name, record_id);
CREATE INDEX idx_audit_log_action ON public.audit_log (action);
CREATE INDEX idx_audit_log_created_at ON public.audit_log (created_at);
-- =============================================================================
-- 2. Immutability triggers: block UPDATE and DELETE on audit_log
-- =============================================================================
CREATE OR REPLACE FUNCTION public.audit_log_immutable()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
RAISE EXCEPTION 'Audit log entries cannot be modified or deleted';
END;
$$;
CREATE TRIGGER audit_log_no_update
BEFORE UPDATE ON public.audit_log
FOR EACH ROW EXECUTE FUNCTION public.audit_log_immutable();
CREATE TRIGGER audit_log_no_delete
BEFORE DELETE ON public.audit_log
FOR EACH ROW EXECUTE FUNCTION public.audit_log_immutable();