# Entity Submission Flow - CRITICAL SECURITY PATTERN ## The Sacred Flow Every entity creation or edit MUST follow this exact flow: ``` 1. User fills form ↓ 2. Submission created → moderation queue ↓ 3. Moderator reviews & approves ↓ 4. Edge function writes to database (via service role) ↓ 5. Versioning system captures change ↓ 6. Public can view live entity ``` ## ❌ NEVER DO THIS ```ts // Direct database write - BYPASSES EVERYTHING! await supabase.from('parks').insert(parkData); await supabase.from('rides').update({ id: rideId }, rideData); // Even if you're a moderator/admin: if (isAdmin) { await supabase.from('parks').insert(parkData); // STILL WRONG! } ``` **Why this is blocked:** - RLS policies explicitly deny INSERT/UPDATE to authenticated users - Only the service role (edge functions) can write - Edge functions are ONLY triggered by moderation approval ## ✅ ALWAYS DO THIS ```ts // Import the submission helpers import { submitParkCreation, submitParkUpdate, submitRideCreation, submitRideUpdate } from '@/lib/entitySubmissionHelpers'; // For new entities: const result = await submitParkCreation(parkData, user.id); // For editing existing entities: const result = await submitParkUpdate(parkId, parkData, user.id); ``` ## For React Components Use the enforced wrappers: ```tsx import { enforceParkSubmissionFlow } from '@/lib/entityFormValidation'; // In your component: const handleSubmit = enforceParkSubmissionFlow(isEditing, park?.id); { await handleSubmit(data, user!.id); toast({ title: "Submitted for review" }); }} /> ``` ## Why This Matters ### Security - Prevents spam and vandalism - Rate-limits submissions via queue system - Blocks malicious automated bots ### Quality - Human review catches errors, duplicates, inappropriate content - Maintains high data quality standards ### Accountability - Full version history with user attribution - Every change is tracked and auditable - Can rollback any change ### Compliance - Complete audit trail for legal/regulatory requirements - User attribution for all content ## For Moderators/Admins **Even moderators must follow the flow:** 1. Create/edit submission through normal UI 2. It goes to moderation queue 3. Review your own submission 4. Approve it **Why?** - Ensures versioning captures the change - Creates audit trail - Prevents accidental mistakes **Exception:** Only emergency database fixes via SQL editor should bypass this. These should be: - Extremely rare - Documented in admin_audit_log - Followed by manual version creation if needed ## Database Protection ### RLS Policies The following tables have explicit denial policies: - `parks` - INSERT/UPDATE blocked for authenticated users - `rides` - INSERT/UPDATE blocked for authenticated users - `companies` - INSERT/UPDATE blocked for authenticated users - `ride_models` - INSERT/UPDATE blocked for authenticated users ### Service Role Access Only these edge functions can write (they use service role): - `process-selective-approval` - Applies approved submissions atomically (PostgreSQL transaction RPC) - Direct SQL migrations (admin only) ### Versioning Triggers All entity writes trigger `auto_create_entity_version()`: - Captures full entity state - Records user who made change - Creates diff of what changed - Logs to audit trail if user attribution missing ## Error Messages If you see these errors, you're trying to bypass the flow: ``` Error: new row violates row-level security policy ``` → You tried to INSERT/UPDATE directly. Use submission helpers. ``` Error: permission denied for table parks/rides/companies/ride_models ``` → Same issue - use submission helpers. ## Testing the Flow ### Manual Test: 1. Create a new park through the UI 2. Check `content_submissions` table - should have new row 3. Check `submission_items` table - should have park data 4. Approve via moderation queue 5. Check `parks` table - entity should appear 6. Check `entity_versions` table - version should be created 7. Version should have correct `changed_by` user_id ### Automated Test: ```ts // Test that direct writes are blocked try { await supabase.from('parks').insert({ name: 'Test Park' }); throw new Error('Expected RLS to block this'); } catch (error) { // Should get permission denied error expect(error.message).toContain('row-level security'); } // Test that submission flow works const result = await submitParkCreation(parkData, userId); expect(result.submitted).toBe(true); ``` ## Monitoring ### Dashboard Widgets Check the admin dashboard for: - **Suspicious Versions** - Versions without user attribution - **Queue Backlog** - Submissions waiting too long - **Flow Violations** - Any attempts to bypass the flow ### Audit Logs ```sql -- Check for versions without user attribution SELECT * FROM entity_versions WHERE changed_by IS NULL ORDER BY changed_at DESC; -- Check for direct writes (should be none except migrations) SELECT * FROM admin_audit_log WHERE action = 'version_without_user' ORDER BY created_at DESC; ``` ## Common Pitfalls ### ❌ "I'll just fix this one typo directly" Even small fixes must go through the flow. Creates version history. ### ❌ "I'm an admin, I can bypass this" No. Admins follow the flow. Prevents mistakes and maintains audit trail. ### ❌ "This is taking too long, I'll do it in SQL editor" Only for emergencies. Document it. Create manual version if needed. ### ❌ "I'll bulk import via CSV" Bulk imports should: 1. Create submissions programmatically 2. Auto-approve them if from trusted source 3. Still trigger versioning ## Questions? **Q: What if I need to bulk import 1000 parks?** A: Create submissions programmatically, set `status='approved'`, let edge function process them. **Q: What if there's an emergency typo on homepage?** A: Make the fix via submission → have another mod approve immediately. Takes <1 minute. **Q: What if the moderation queue is broken?** A: Fix the queue. Don't bypass the flow. That creates worse problems. **Q: Can I use the service role key directly?** A: Only in edge functions. Never in client-side code. Never for routine edits. ## Related Files - `src/lib/entitySubmissionHelpers.ts` - Core submission functions - `src/lib/entityFormValidation.ts` - Enforced wrappers - `supabase/functions/process-selective-approval/index.ts` - Atomic transaction RPC approval processor - `src/components/admin/*Form.tsx` - Form components using the flow - `docs/ATOMIC_APPROVAL_TRANSACTIONS.md` - Atomic transaction RPC documentation ## Update History - 2025-01-XX: Added explicit RLS denial policies - 2025-01-XX: Added versioning for photos table - 2025-01-XX: Added user attribution to edge function - 2025-01-XX: Created this documentation