294 lines
12 KiB
PL/PgSQL
294 lines
12 KiB
PL/PgSQL
-- ── SECURITY DEFINER helpers ──────────────────────────────────────────────────
|
|
|
|
-- Retorna el org_id del usuario actual (null para usuarios sin org asignada)
|
|
CREATE OR REPLACE FUNCTION public.current_org()
|
|
RETURNS uuid LANGUAGE sql STABLE SECURITY DEFINER AS $$
|
|
SELECT org_id FROM public.users WHERE id = auth.uid()
|
|
$$;
|
|
|
|
-- Retorna true si el usuario pertenece a la organización proveedora (InQuality)
|
|
CREATE OR REPLACE FUNCTION public.is_inquality()
|
|
RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER AS $$
|
|
SELECT EXISTS (
|
|
SELECT 1 FROM public.users u
|
|
JOIN public.organizations o ON o.id = u.org_id
|
|
WHERE u.id = auth.uid() AND o.is_provider = true
|
|
)
|
|
$$;
|
|
|
|
-- Retorna el platform_role del usuario actual
|
|
CREATE OR REPLACE FUNCTION public.current_platform_role()
|
|
RETURNS text LANGUAGE sql STABLE SECURITY DEFINER AS $$
|
|
SELECT platform_role FROM public.users WHERE id = auth.uid()
|
|
$$;
|
|
|
|
-- ── Trigger handle_new_user ───────────────────────────────────────────────────
|
|
-- Auto-crea fila en public.users para usuarios @inquality.com.py que se loguean
|
|
-- con Google OAuth. Los usuarios clientes se crean via Edge Function (invite),
|
|
-- no por este trigger.
|
|
-- ON CONFLICT (id) DO NOTHING: no sobrescribir si la Edge Function ya creó la fila.
|
|
|
|
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
|
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$
|
|
DECLARE
|
|
provider_org_id uuid;
|
|
BEGIN
|
|
IF new.email LIKE '%@inquality.com.py' THEN
|
|
SELECT id INTO provider_org_id
|
|
FROM public.organizations
|
|
WHERE is_provider = true
|
|
LIMIT 1;
|
|
|
|
INSERT INTO public.users (id, name, avatar_url, platform_role, org_id)
|
|
VALUES (
|
|
new.id,
|
|
COALESCE(new.raw_user_meta_data->>'full_name', new.email),
|
|
new.raw_user_meta_data->>'avatar_url',
|
|
'member',
|
|
provider_org_id
|
|
)
|
|
ON CONFLICT (id) DO NOTHING;
|
|
END IF;
|
|
RETURN new;
|
|
END;
|
|
$$;
|
|
|
|
-- Crear trigger (DROP IF EXISTS para idempotencia)
|
|
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
|
|
CREATE TRIGGER on_auth_user_created
|
|
AFTER INSERT ON auth.users
|
|
FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user();
|
|
|
|
-- ── RLS: organizations ────────────────────────────────────────────────────────
|
|
|
|
CREATE POLICY "orgs_select" ON public.organizations
|
|
FOR SELECT USING (
|
|
public.is_inquality()
|
|
OR id = public.current_org()
|
|
);
|
|
|
|
CREATE POLICY "orgs_insert" ON public.organizations
|
|
FOR INSERT WITH CHECK (public.is_platform_admin());
|
|
|
|
CREATE POLICY "orgs_update" ON public.organizations
|
|
FOR UPDATE USING (public.is_platform_admin());
|
|
|
|
CREATE POLICY "orgs_delete" ON public.organizations
|
|
FOR DELETE USING (public.is_platform_admin());
|
|
|
|
-- ── RLS: users ───────────────────────────────────────────────────────────────
|
|
-- Reemplazar la política permisiva "read_users" (migration 001) con políticas granulares
|
|
|
|
DROP POLICY IF EXISTS "read_users" ON public.users;
|
|
|
|
CREATE POLICY "users_select_own" ON public.users
|
|
FOR SELECT USING (id = auth.uid());
|
|
|
|
CREATE POLICY "users_select_admin" ON public.users
|
|
FOR SELECT USING (public.is_platform_admin());
|
|
|
|
-- ── RLS: processes ────────────────────────────────────────────────────────────
|
|
-- Reemplazar las tres políticas de migration 012 con versiones que incluyen acceso por org
|
|
|
|
DROP POLICY IF EXISTS "select_processes" ON public.processes;
|
|
|
|
CREATE POLICY "select_processes" ON public.processes
|
|
FOR SELECT USING (
|
|
public.is_platform_admin()
|
|
OR owner_id = auth.uid()
|
|
OR (
|
|
public.current_org() IS NOT NULL
|
|
AND org_id = public.current_org()
|
|
)
|
|
OR EXISTS (
|
|
SELECT 1 FROM public.process_access
|
|
WHERE process_id = processes.id
|
|
AND (
|
|
(grantee_type = 'user' AND grantee_id = auth.uid())
|
|
OR (grantee_type = 'group' AND grantee_id IN (
|
|
SELECT group_id FROM public.group_memberships WHERE user_id = auth.uid()
|
|
))
|
|
)
|
|
)
|
|
);
|
|
|
|
DROP POLICY IF EXISTS "insert_processes" ON public.processes;
|
|
|
|
CREATE POLICY "insert_processes" ON public.processes
|
|
FOR INSERT WITH CHECK (
|
|
public.is_platform_admin()
|
|
OR public.current_platform_role() = 'member'
|
|
-- client_editor y client_viewer NO pueden crear procesos nuevos
|
|
);
|
|
|
|
DROP POLICY IF EXISTS "update_processes" ON public.processes;
|
|
|
|
CREATE POLICY "update_processes" ON public.processes
|
|
FOR UPDATE USING (
|
|
public.is_platform_admin()
|
|
OR owner_id = auth.uid()
|
|
OR (
|
|
public.current_platform_role() = 'client_editor'
|
|
AND org_id = public.current_org()
|
|
)
|
|
OR EXISTS (
|
|
SELECT 1 FROM public.process_access
|
|
WHERE process_id = processes.id AND permission = 'edit'
|
|
AND (
|
|
(grantee_type = 'user' AND grantee_id = auth.uid())
|
|
OR (grantee_type = 'group' AND grantee_id IN (
|
|
SELECT group_id FROM public.group_memberships WHERE user_id = auth.uid()
|
|
))
|
|
)
|
|
)
|
|
);
|
|
|
|
-- ── RLS: activities ──────────────────────────────────────────────────────────
|
|
-- Reemplazar "all_activities" (migration 006) con políticas granulares por rol
|
|
|
|
DROP POLICY IF EXISTS "all_activities" ON public.activities;
|
|
|
|
-- SELECT: acceso transitivo — si el usuario puede ver el proceso padre (via processes SELECT policy),
|
|
-- puede ver sus actividades. El EXISTS aplica RLS de processes automáticamente.
|
|
CREATE POLICY "activities_select" ON public.activities
|
|
FOR SELECT USING (
|
|
EXISTS (SELECT 1 FROM public.processes WHERE id = activities.process_id)
|
|
);
|
|
|
|
-- INSERT / UPDATE: InQuality + client_editor pueden escribir; client_viewer y guest no
|
|
CREATE POLICY "activities_insert" ON public.activities
|
|
FOR INSERT WITH CHECK (
|
|
EXISTS (SELECT 1 FROM public.processes WHERE id = activities.process_id)
|
|
AND public.current_platform_role() NOT IN ('client_viewer', 'guest')
|
|
);
|
|
|
|
CREATE POLICY "activities_update" ON public.activities
|
|
FOR UPDATE USING (
|
|
EXISTS (SELECT 1 FROM public.processes WHERE id = activities.process_id)
|
|
AND public.current_platform_role() NOT IN ('client_viewer', 'guest')
|
|
);
|
|
|
|
-- DELETE: solo InQuality (platform_admin o member)
|
|
CREATE POLICY "activities_delete" ON public.activities
|
|
FOR DELETE USING (
|
|
EXISTS (SELECT 1 FROM public.processes WHERE id = activities.process_id)
|
|
AND public.current_platform_role() IN ('platform_admin', 'member')
|
|
);
|
|
|
|
-- ── RLS: resources ────────────────────────────────────────────────────────────
|
|
-- Reemplazar "all_resources" (migration 005) con políticas granulares por rol.
|
|
-- NOTA: la tabla resources no tiene columna process_id en este schema — los recursos
|
|
-- son catálogo global. La policy SELECT mantiene acceso a todos los usuarios autenticados
|
|
-- (equivalente a la política original) y se alinea con supabaseResourceRepo.getAll().
|
|
|
|
DROP POLICY IF EXISTS "all_resources" ON public.resources;
|
|
|
|
CREATE POLICY "resources_select" ON public.resources
|
|
FOR SELECT USING (auth.uid() IS NOT NULL);
|
|
|
|
CREATE POLICY "resources_insert" ON public.resources
|
|
FOR INSERT WITH CHECK (
|
|
auth.uid() IS NOT NULL
|
|
AND public.current_platform_role() NOT IN ('client_viewer', 'guest')
|
|
);
|
|
|
|
CREATE POLICY "resources_update" ON public.resources
|
|
FOR UPDATE USING (
|
|
auth.uid() IS NOT NULL
|
|
AND public.current_platform_role() NOT IN ('client_viewer', 'guest')
|
|
);
|
|
|
|
CREATE POLICY "resources_delete" ON public.resources
|
|
FOR DELETE USING (
|
|
public.current_platform_role() IN ('platform_admin', 'member')
|
|
);
|
|
|
|
-- ── RLS: simulations ─────────────────────────────────────────────────────────
|
|
-- Reemplazar "all_simulations" (migration 009) con políticas granulares por rol
|
|
|
|
DROP POLICY IF EXISTS "all_simulations" ON public.simulations;
|
|
|
|
CREATE POLICY "simulations_select" ON public.simulations
|
|
FOR SELECT USING (
|
|
EXISTS (SELECT 1 FROM public.processes WHERE id = simulations.process_id)
|
|
);
|
|
|
|
CREATE POLICY "simulations_insert" ON public.simulations
|
|
FOR INSERT WITH CHECK (
|
|
EXISTS (SELECT 1 FROM public.processes WHERE id = simulations.process_id)
|
|
AND public.current_platform_role() NOT IN ('client_viewer', 'guest')
|
|
);
|
|
|
|
CREATE POLICY "simulations_delete" ON public.simulations
|
|
FOR DELETE USING (
|
|
EXISTS (SELECT 1 FROM public.processes WHERE id = simulations.process_id)
|
|
AND public.current_platform_role() IN ('platform_admin', 'member')
|
|
);
|
|
|
|
-- ── RLS: activity_resource_assignments ───────────────────────────────────────
|
|
-- Reemplazar "all_assignments" (migration 007 — nombre real, no "all_activity_resource_assignments")
|
|
-- con políticas granulares. Heredan acceso del proceso via actividad padre.
|
|
|
|
DROP POLICY IF EXISTS "all_assignments" ON public.activity_resource_assignments;
|
|
|
|
CREATE POLICY "ara_select" ON public.activity_resource_assignments
|
|
FOR SELECT USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.activities a
|
|
JOIN public.processes p ON p.id = a.process_id
|
|
WHERE a.id = activity_resource_assignments.activity_id
|
|
)
|
|
);
|
|
|
|
CREATE POLICY "ara_write" ON public.activity_resource_assignments
|
|
FOR INSERT WITH CHECK (
|
|
EXISTS (
|
|
SELECT 1 FROM public.activities a
|
|
JOIN public.processes p ON p.id = a.process_id
|
|
WHERE a.id = activity_resource_assignments.activity_id
|
|
)
|
|
AND public.current_platform_role() NOT IN ('client_viewer', 'guest')
|
|
);
|
|
|
|
CREATE POLICY "ara_update" ON public.activity_resource_assignments
|
|
FOR UPDATE USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.activities a
|
|
JOIN public.processes p ON p.id = a.process_id
|
|
WHERE a.id = activity_resource_assignments.activity_id
|
|
)
|
|
AND public.current_platform_role() NOT IN ('client_viewer', 'guest')
|
|
);
|
|
|
|
CREATE POLICY "ara_delete" ON public.activity_resource_assignments
|
|
FOR DELETE USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.activities a
|
|
JOIN public.processes p ON p.id = a.process_id
|
|
WHERE a.id = activity_resource_assignments.activity_id
|
|
)
|
|
AND public.current_platform_role() NOT IN ('client_viewer', 'guest')
|
|
);
|
|
|
|
-- ── RLS: gateways ────────────────────────────────────────────────────────────
|
|
-- Reemplazar "all_gateways" (migration 008) con políticas granulares por rol
|
|
|
|
DROP POLICY IF EXISTS "all_gateways" ON public.gateways;
|
|
|
|
CREATE POLICY "gateways_select" ON public.gateways
|
|
FOR SELECT USING (
|
|
EXISTS (SELECT 1 FROM public.processes WHERE id = gateways.process_id)
|
|
);
|
|
|
|
CREATE POLICY "gateways_write" ON public.gateways
|
|
FOR INSERT WITH CHECK (
|
|
EXISTS (SELECT 1 FROM public.processes WHERE id = gateways.process_id)
|
|
AND public.current_platform_role() NOT IN ('client_viewer', 'guest')
|
|
);
|
|
|
|
CREATE POLICY "gateways_update" ON public.gateways
|
|
FOR UPDATE USING (
|
|
EXISTS (SELECT 1 FROM public.processes WHERE id = gateways.process_id)
|
|
AND public.current_platform_role() NOT IN ('client_viewer', 'guest')
|
|
);
|