From 02a9fdad84a42dd57707456b7fce0fa9fc8d8734 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:46:52 +0000 Subject: [PATCH] Refactor: Implement submission flow enforcement --- src/integrations/supabase/types.ts | 8 + ...4_ae59dca7-e906-41b7-b1a1-002e62d2fa63.sql | 145 ++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 supabase/migrations/20251006164634_ae59dca7-e906-41b7-b1a1-002e62d2fa63.sql diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index 90861207..710d5256 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -2700,6 +2700,14 @@ export type Database = { } Returns: string } + set_config_value: { + Args: { + is_local?: boolean + setting_name: string + setting_value: string + } + Returns: undefined + } update_company_ratings: { Args: { target_company_id: string } Returns: undefined diff --git a/supabase/migrations/20251006164634_ae59dca7-e906-41b7-b1a1-002e62d2fa63.sql b/supabase/migrations/20251006164634_ae59dca7-e906-41b7-b1a1-002e62d2fa63.sql new file mode 100644 index 00000000..bd608a3a --- /dev/null +++ b/supabase/migrations/20251006164634_ae59dca7-e906-41b7-b1a1-002e62d2fa63.sql @@ -0,0 +1,145 @@ +-- Phase 1.1: Add Explicit INSERT/UPDATE Denial Policies +-- These policies make it IMPOSSIBLE to bypass the submission flow + +-- Parks table - block direct writes +CREATE POLICY "Deny direct inserts to parks" +ON public.parks FOR INSERT +TO authenticated +WITH CHECK (false); + +CREATE POLICY "Deny direct updates to parks" +ON public.parks FOR UPDATE +TO authenticated +USING (false); + +-- Rides table - block direct writes +CREATE POLICY "Deny direct inserts to rides" +ON public.rides FOR INSERT +TO authenticated +WITH CHECK (false); + +CREATE POLICY "Deny direct updates to rides" +ON public.rides FOR UPDATE +TO authenticated +USING (false); + +-- Companies table - block direct writes +CREATE POLICY "Deny direct inserts to companies" +ON public.companies FOR INSERT +TO authenticated +WITH CHECK (false); + +CREATE POLICY "Deny direct updates to companies" +ON public.companies FOR UPDATE +TO authenticated +USING (false); + +-- Ride models table - block direct writes +CREATE POLICY "Deny direct inserts to ride_models" +ON public.ride_models FOR INSERT +TO authenticated +WITH CHECK (false); + +CREATE POLICY "Deny direct updates to ride_models" +ON public.ride_models FOR UPDATE +TO authenticated +USING (false); + +-- Phase 1.2: Add Versioning for Photos Table +DROP TRIGGER IF EXISTS auto_version_photos ON public.photos; +CREATE TRIGGER auto_version_photos + AFTER INSERT OR UPDATE ON public.photos + FOR EACH ROW + EXECUTE FUNCTION public.auto_create_entity_version(); + +-- Update auto_create_entity_version to handle photos +CREATE OR REPLACE FUNCTION public.auto_create_entity_version() +RETURNS trigger +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public +AS $function$ +DECLARE + v_entity_type TEXT; + v_change_type version_change_type; + v_user_id UUID; + v_version_data JSONB; +BEGIN + -- Determine entity type from table name + v_entity_type := CASE TG_TABLE_NAME + WHEN 'parks' THEN 'park' + WHEN 'rides' THEN 'ride' + WHEN 'companies' THEN 'company' + WHEN 'ride_models' THEN 'ride_model' + WHEN 'photos' THEN 'photo' + ELSE substring(TG_TABLE_NAME from 1 for length(TG_TABLE_NAME) - 1) + END; + + -- Determine change type + v_change_type := CASE TG_OP + WHEN 'INSERT' THEN 'created'::version_change_type + WHEN 'UPDATE' THEN 'updated'::version_change_type + ELSE 'updated'::version_change_type + END; + + -- Get user from session or auth context + BEGIN + v_user_id := current_setting('app.current_user_id', true)::UUID; + EXCEPTION WHEN OTHERS THEN + v_user_id := auth.uid(); + END; + + -- Convert NEW record to JSONB + v_version_data := to_jsonb(NEW); + + -- Create version (only if we have a user context) + IF v_user_id IS NOT NULL THEN + PERFORM public.create_entity_version( + v_entity_type, + NEW.id, + v_version_data, + v_user_id, + CASE TG_OP + WHEN 'INSERT' THEN 'Entity created' + WHEN 'UPDATE' THEN 'Entity updated' + ELSE 'Entity modified' + END, + NULL, + v_change_type + ); + ELSE + -- Log suspicious version without user + INSERT INTO admin_audit_log ( + action, + details, + created_at + ) VALUES ( + 'version_without_user', + jsonb_build_object( + 'entity_type', v_entity_type, + 'entity_id', NEW.id, + 'table', TG_TABLE_NAME, + 'operation', TG_OP + ), + NOW() + ); + END IF; + + RETURN NEW; +END; +$function$; + +-- Phase 1.3: Create set_config helper for edge function +CREATE OR REPLACE FUNCTION public.set_config_value( + setting_name text, + setting_value text, + is_local boolean DEFAULT false +) +RETURNS void +LANGUAGE plpgsql +SECURITY DEFINER +AS $$ +BEGIN + PERFORM set_config(setting_name, setting_value, is_local); +END; +$$; \ No newline at end of file