Primer commit - Sprint 1
This commit is contained in:
87
supabase/migrations/20260531000004_constraints_triggers.sql
Normal file
87
supabase/migrations/20260531000004_constraints_triggers.sql
Normal file
@@ -0,0 +1,87 @@
|
||||
-- ============================================================
|
||||
-- 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();
|
||||
Reference in New Issue
Block a user