mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 10:51:13 -05:00
244 lines
6.6 KiB
Markdown
244 lines
6.6 KiB
Markdown
# 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);
|
|
|
|
<ParkForm
|
|
isEditing={isEditing}
|
|
initialData={park}
|
|
onSubmit={async (data) => {
|
|
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
|
|
- 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` - Approval processor
|
|
- `src/components/admin/*Form.tsx` - Form components using the flow
|
|
|
|
## 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
|