Files
hackathon-innovaflow5.0-cdf…/backend/app/db/rls_policies.sql

170 lines
6.7 KiB
SQL

-- ============================================================
-- RLS Policies — Sistema de Recolección Inteligente
-- Ejecutar en: Supabase > SQL Editor
-- Regla innegociable: el ciudadano NUNCA ve coordenadas ni datos ajenos.
-- ============================================================
-- ------------------------------------------------------------
-- 1. Tabla public.users (espejo de auth.users con rol)
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.users (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
email TEXT,
phone TEXT,
role TEXT DEFAULT 'citizen'
CHECK (role IN ('citizen', 'driver', 'admin')),
created_at TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE public.users ENABLE ROW LEVEL SECURITY;
-- Cada usuario ve y edita solo su propia fila
DROP POLICY IF EXISTS "users_select_own" ON public.users;
CREATE POLICY "users_select_own" ON public.users
FOR SELECT USING (auth.uid() = id);
DROP POLICY IF EXISTS "users_update_own" ON public.users;
CREATE POLICY "users_update_own" ON public.users
FOR UPDATE USING (auth.uid() = id);
-- El backend (service_role) inserta al registrar; no necesita policy
-- porque service_role bypasea RLS por diseño.
-- ------------------------------------------------------------
-- 2. Tabla public.addresses
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.addresses (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
label TEXT NOT NULL,
calle TEXT NOT NULL,
colonia TEXT NOT NULL,
route_id TEXT NOT NULL,
verified BOOLEAN DEFAULT FALSE,
verified_method TEXT,
verified_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE public.addresses ENABLE ROW LEVEL SECURITY;
-- Ciudadano: solo lee sus domicilios; admin lee todos
DROP POLICY IF EXISTS "addresses_select" ON public.addresses;
CREATE POLICY "addresses_select" ON public.addresses
FOR SELECT USING (
auth.uid() = user_id
OR (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin'
);
-- Ciudadano solo inserta domicilios propios
DROP POLICY IF EXISTS "addresses_insert" ON public.addresses;
CREATE POLICY "addresses_insert" ON public.addresses
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- Ciudadano solo modifica los suyos (para verified=true tras OCR)
DROP POLICY IF EXISTS "addresses_update" ON public.addresses;
CREATE POLICY "addresses_update" ON public.addresses
FOR UPDATE USING (
auth.uid() = user_id
OR (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin'
);
-- ------------------------------------------------------------
-- 3. Tabla public.route_positions ← ÚNICA QUE TIENE LAT/LNG
-- Solo admin puede leerla. Regla innegociable #1 y #4.
-- ------------------------------------------------------------
ALTER TABLE public.route_positions ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "route_positions_admin_only" ON public.route_positions;
CREATE POLICY "route_positions_admin_only" ON public.route_positions
FOR SELECT USING (
(SELECT role FROM public.users WHERE id = auth.uid()) = 'admin'
);
-- Sin policy para INSERT/UPDATE/DELETE → el backend usa service_role para el seed.
-- ------------------------------------------------------------
-- 4. Tabla public.notifications
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.notifications (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES public.users(id) ON DELETE CASCADE,
route_id TEXT,
type TEXT,
payload JSONB,
sent_at TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "notifications_select" ON public.notifications;
CREATE POLICY "notifications_select" ON public.notifications
FOR SELECT USING (
auth.uid() = user_id
OR (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin'
);
-- ------------------------------------------------------------
-- 5. Tabla public.feedback
-- Las quejas van a target_unit_id (unidad), NUNCA al chofer.
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.feedback (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES public.users(id) ON DELETE SET NULL,
address_id UUID REFERENCES public.addresses(id) ON DELETE SET NULL,
type TEXT,
target_unit_id INT, -- unidad (no chofer): privacidad del chofer
message TEXT,
rating SMALLINT CHECK (rating BETWEEN 1 AND 5),
created_at TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE public.feedback ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "feedback_select" ON public.feedback;
CREATE POLICY "feedback_select" ON public.feedback
FOR SELECT USING (
auth.uid() = user_id
OR (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin'
);
DROP POLICY IF EXISTS "feedback_insert" ON public.feedback;
CREATE POLICY "feedback_insert" ON public.feedback
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- ------------------------------------------------------------
-- 5b. Tabla public.incidents (reportes ciudadanos)
-- ------------------------------------------------------------
ALTER TABLE public.incidents ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "incidents_select" ON public.incidents;
CREATE POLICY "incidents_select" ON public.incidents
FOR SELECT USING (
auth.uid() = user_id
OR (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin'
);
DROP POLICY IF EXISTS "incidents_insert" ON public.incidents;
CREATE POLICY "incidents_insert" ON public.incidents
FOR INSERT WITH CHECK (auth.uid() = user_id);
DROP POLICY IF EXISTS "incidents_update_admin" ON public.incidents;
CREATE POLICY "incidents_update_admin" ON public.incidents
FOR UPDATE USING (
(SELECT role FROM public.users WHERE id = auth.uid()) = 'admin'
);
-- ------------------------------------------------------------
-- 6. Índices útiles para rendimiento
-- ------------------------------------------------------------
CREATE INDEX IF NOT EXISTS idx_addresses_user_id ON public.addresses(user_id);
CREATE INDEX IF NOT EXISTS idx_addresses_route_id ON public.addresses(route_id);
CREATE INDEX IF NOT EXISTS idx_notifications_user ON public.notifications(user_id);
CREATE INDEX IF NOT EXISTS idx_feedback_user ON public.feedback(user_id);