Files
motor-de-encuestas/supabase/migrations/20260531000004_constraints_triggers.sql
2026-05-31 04:33:07 -03:00

88 lines
3.1 KiB
PL/PgSQL

-- ============================================================
-- Migration 004: Constraints y triggers de privacidad
--
-- 1. Trigger: rechazar respondent_id no nulo en encuestas anónimas
-- → Regla no negociable #2 de CLAUDE.md (barrera técnica)
--
-- 2. Trigger: inmutabilidad de consent_records
-- → ADR-003: sin UPDATE/DELETE, evidencia permanente del consentimiento
--
-- 3. Trigger: inmutabilidad de consent_versions
-- → Textos de consentimiento históricos son inmutables
-- ============================================================
-- ============================================================
-- 1. Trigger: respondent_id prohibido en encuestas anónimas
-- ============================================================
CREATE OR REPLACE FUNCTION public.enforce_anonymous_no_respondent_id()
RETURNS trigger
LANGUAGE plpgsql
SET search_path = public
AS $$
DECLARE
v_is_anonymous boolean;
BEGIN
-- Leer la propiedad de la encuesta referida
SELECT is_anonymous INTO v_is_anonymous
FROM public.surveys
WHERE id = NEW.survey_id;
IF v_is_anonymous AND NEW.respondent_id IS NOT NULL THEN
RAISE EXCEPTION
'Privacy violation: respondent_id must be NULL in anonymous surveys (survey_id: %). '
'See CLAUDE.md rule #2.',
NEW.survey_id
USING ERRCODE = 'check_violation';
END IF;
RETURN NEW;
END;
$$;
CREATE TRIGGER trg_responses_anonymous_no_respondent_id
BEFORE INSERT OR UPDATE ON public.responses
FOR EACH ROW EXECUTE FUNCTION public.enforce_anonymous_no_respondent_id();
-- ============================================================
-- 2. Trigger: inmutabilidad de consent_records (ADR-003)
-- El consentimiento es evidencia, no UI. Nunca se modifica.
-- ============================================================
CREATE OR REPLACE FUNCTION public.deny_consent_records_mutation()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
RAISE EXCEPTION
'consent_records is immutable: % is not permitted. '
'Each response consent is permanent evidence (ADR-003).',
TG_OP
USING ERRCODE = 'insufficient_privilege';
END;
$$;
CREATE TRIGGER trg_consent_records_immutable
BEFORE UPDATE OR DELETE ON public.consent_records
FOR EACH ROW EXECUTE FUNCTION public.deny_consent_records_mutation();
-- ============================================================
-- 3. Trigger: inmutabilidad de consent_versions
-- Los textos históricos no se alteran. Cambiar el wording del
-- consentimiento crea una nueva versión, nunca edita la existente.
-- ============================================================
CREATE OR REPLACE FUNCTION public.deny_consent_versions_mutation()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
RAISE EXCEPTION
'consent_versions is immutable: % is not permitted. '
'Create a new version instead of modifying an existing one.',
TG_OP
USING ERRCODE = 'insufficient_privilege';
END;
$$;
CREATE TRIGGER trg_consent_versions_immutable
BEFORE UPDATE OR DELETE ON public.consent_versions
FOR EACH ROW EXECUTE FUNCTION public.deny_consent_versions_mutation();