|
| 1 | +-- ═══════════════════════════════════════════════════════════════════════════════ |
| 2 | +-- SIMPATICO HR — COMPANIES TABLE RLS + BYOK COLUMN PROTECTION |
| 3 | +-- ═══════════════════════════════════════════════════════════════════════════════ |
| 4 | +-- Fixes Critical Issue C5: The `companies` table had NO Row-Level Security. |
| 5 | +-- This migration enables RLS and adds tenant-scoped policies to prevent |
| 6 | +-- cross-tenant access to BYOK AI configuration (api keys, provider, model). |
| 7 | +-- ═══════════════════════════════════════════════════════════════════════════════ |
| 8 | + |
| 9 | +-- 1. Enable RLS on the companies table |
| 10 | +ALTER TABLE public.companies ENABLE ROW LEVEL SECURITY; |
| 11 | + |
| 12 | +-- 2. Service role bypass (Worker API uses service_role for backend operations) |
| 13 | +CREATE POLICY "service_full_access" ON public.companies |
| 14 | + FOR ALL TO service_role |
| 15 | + USING (true) |
| 16 | + WITH CHECK (true); |
| 17 | + |
| 18 | +-- 3. Authenticated users can only read their own company row |
| 19 | +-- The tenant_id in JWT claims must match the company's id |
| 20 | +CREATE POLICY "tenant_read_own_company" ON public.companies |
| 21 | + FOR SELECT TO authenticated |
| 22 | + USING ( |
| 23 | + id = COALESCE( |
| 24 | + (auth.jwt()->'app_metadata'->>'tenant_id'), |
| 25 | + (auth.jwt()->'app_metadata'->>'company_id'), |
| 26 | + (auth.jwt()->'user_metadata'->>'tenant_id'), |
| 27 | + (auth.jwt()->'user_metadata'->>'company_id'), |
| 28 | + 'SIMP_PRO_MAIN' |
| 29 | + ) |
| 30 | + ); |
| 31 | + |
| 32 | +-- 4. Only service_role (backend Worker) can update company rows |
| 33 | +-- Authenticated users must go through the Worker API endpoints |
| 34 | +-- which enforce role checks (admin, company_admin, etc.) |
| 35 | +CREATE POLICY "tenant_update_own_company" ON public.companies |
| 36 | + FOR UPDATE TO authenticated |
| 37 | + USING ( |
| 38 | + id = COALESCE( |
| 39 | + (auth.jwt()->'app_metadata'->>'tenant_id'), |
| 40 | + (auth.jwt()->'app_metadata'->>'company_id'), |
| 41 | + (auth.jwt()->'user_metadata'->>'tenant_id'), |
| 42 | + (auth.jwt()->'user_metadata'->>'company_id'), |
| 43 | + 'SIMP_PRO_MAIN' |
| 44 | + ) |
| 45 | + ) |
| 46 | + WITH CHECK ( |
| 47 | + id = COALESCE( |
| 48 | + (auth.jwt()->'app_metadata'->>'tenant_id'), |
| 49 | + (auth.jwt()->'app_metadata'->>'company_id'), |
| 50 | + (auth.jwt()->'user_metadata'->>'tenant_id'), |
| 51 | + (auth.jwt()->'user_metadata'->>'company_id'), |
| 52 | + 'SIMP_PRO_MAIN' |
| 53 | + ) |
| 54 | + ); |
| 55 | + |
| 56 | +-- ═══════════════════════════════════════════════════════════════════════════════ |
| 57 | +-- DONE. After running this migration: |
| 58 | +-- 1. The `companies` table now has RLS enabled |
| 59 | +-- 2. Authenticated users can only read/update their OWN company row |
| 60 | +-- 3. The backend Worker (service_role) retains full access for admin operations |
| 61 | +-- 4. Direct Supabase API calls with anon/authenticated keys are now tenant-scoped |
| 62 | +-- ═══════════════════════════════════════════════════════════════════════════════ |
0 commit comments