-- ============================================================================= -- Migration: 20260419000017_addons_central_saas_hardening -- Sessão 8 — Addons + Central SaaS scan. -- -- Resolve: -- • Addons V#1 (CRÍTICO — dinheiro real): addon_transactions sem WITH CHECK -- • Addons V#2: addon_credits sem CHECK contra saldo negativo -- • Central SaaS V#1: saas_faq write permite tenant_admin/clinic_admin -- -- Auditoria prévia: 0 addon_credits com balance < 0 (seguro CHECK). -- Edge functions consomem créditos via service_role (bypass RLS) — nova -- restrição não quebra pipeline. -- ============================================================================= -- ───────────────────────────────────────────────────────────────────────── -- Addons V#1: addon_transactions INSERT WITH CHECK (saas_admin only) -- ----------------------------------------------------------------------------- DROP POLICY IF EXISTS addon_transactions_admin_insert ON public.addon_transactions; CREATE POLICY addon_transactions_admin_insert ON public.addon_transactions FOR INSERT TO authenticated WITH CHECK ( EXISTS (SELECT 1 FROM public.saas_admins sa WHERE sa.user_id = auth.uid()) ); -- ───────────────────────────────────────────────────────────────────────── -- Addons V#2: addon_credits CHECK contra saldo negativo -- ----------------------------------------------------------------------------- ALTER TABLE public.addon_credits DROP CONSTRAINT IF EXISTS addon_credits_balance_nonneg_chk; ALTER TABLE public.addon_credits ADD CONSTRAINT addon_credits_balance_nonneg_chk CHECK (balance >= 0); -- Aproveita: total_consumed também não deve ser negativo ALTER TABLE public.addon_credits DROP CONSTRAINT IF EXISTS addon_credits_consumed_nonneg_chk; ALTER TABLE public.addon_credits ADD CONSTRAINT addon_credits_consumed_nonneg_chk CHECK (total_consumed >= 0); ALTER TABLE public.addon_credits DROP CONSTRAINT IF EXISTS addon_credits_purchased_nonneg_chk; ALTER TABLE public.addon_credits ADD CONSTRAINT addon_credits_purchased_nonneg_chk CHECK (total_purchased >= 0); -- ───────────────────────────────────────────────────────────────────────── -- Central SaaS V#1: saas_faq + saas_faq_itens write SÓ saas_admin -- ----------------------------------------------------------------------------- DROP POLICY IF EXISTS faq_admin_write ON public.saas_faq; CREATE POLICY faq_saas_admin_write ON public.saas_faq FOR ALL TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); DROP POLICY IF EXISTS faq_itens_admin_write ON public.saas_faq_itens; CREATE POLICY faq_itens_saas_admin_write ON public.saas_faq_itens FOR ALL TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); -- (Policies de leitura — faq_auth_read, faq_public_read, faq_itens_auth_read — permanecem)