d6eb992f71
Continuacao de 7c20b51. Esta etapa fechou TODA revisao senior do SaaS
(15 areas auditadas) + refator parcial de pacientes.
Ver commit.md para descricao completa por sessao.
# Estado final do projeto
- A# auditoria abertos: 1 (A#31 Deploy real)
- V# verificacoes abertos: 14 (todos medios/baixos adiados com plano)
- Criticos: 0
- Altos: 0
- Vitest: 208/208 (era 192, +16 nos novos composables)
- SQL integration: 33/33
- E2E (Playwright): 5/5
- Areas auditadas: 15
# Highlights
- Documentos 100% fechado (V#50/51/52: portal-paciente policy + content_sha256 + 4 cron jobs retention)
- Tenants V#1 P0: tenant_invites com RLS off + 0 policies (mesmo padrao A#30)
- Calendario 100% fechado: feriados WITH CHECK
- Addons V#1 P0 (dinheiro): addon_transactions WITH CHECK saas_admin
- Central SaaS V#1: faq write so saas_admin (era tenant_admin)
- Servicos/Prontuarios 100% fechado: services/medicos/insurance_plans + cascades
- Pacientes V#9: 2 composables novos (useCep, usePatientSupportContacts) + repo estendido + script extraido (template intocado, fica para quando houver E2E)
# 8 migrations novas neste commit
- 20260419000011_documents_portal_patient_policy.sql
- 20260419000012_documents_content_hash.sql
- 20260419000013_cron_retention_jobs.sql
- 20260419000014_financial_security_hardening.sql
- 20260419000015_communication_security_hardening.sql
- 20260419000016_tenants_calendario_hardening.sql
- 20260419000017_addons_central_saas_hardening.sql
- 20260419000018_servicos_prontuarios_hardening.sql
Total acumulado: 18 migrations (Sessoes 1-10).
# A#31 reformulado pra proxima sessao
"Deploy real" muda escopo: como nao ha cloud Supabase nem secrets reais
ainda (MVP), proxima sessao vira "Preparacao completa pra deploy" (DEPLOY.md,
validar migrations num container limpo, audit edge functions, listar env vars,
script db.cjs deploy-check).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
224 lines
9.4 KiB
SQL
224 lines
9.4 KiB
SQL
-- =============================================================================
|
|
-- Migration: 20260419000018_servicos_prontuarios_hardening
|
|
-- Sessão 9 — Serviços/Prontuários scan.
|
|
--
|
|
-- Resolve:
|
|
-- • Serviços V#1+V#2 (CRÍTICOS): silos por owner em services/medicos/insurance_plans
|
|
-- • Serviços V#3+V#4 (ALTOS): cascade silos em commitment_services/insurance_plan_services
|
|
-- • Serviços V#5: WITH CHECK ausente em commitment_time_logs/determined_*
|
|
--
|
|
-- Padrão validado em 5 áreas anteriores (Documentos/Financeiro/Comunicação/etc):
|
|
-- SELECT tenant_member, INSERT/UPDATE/DELETE owner+saas, com WITH CHECK explícito.
|
|
-- =============================================================================
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#1 services — split em 4 policies
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS "services: owner full access" ON public.services;
|
|
DROP POLICY IF EXISTS "services: select" ON public.services;
|
|
DROP POLICY IF EXISTS "services: insert" ON public.services;
|
|
DROP POLICY IF EXISTS "services: update" ON public.services;
|
|
DROP POLICY IF EXISTS "services: delete" ON public.services;
|
|
|
|
CREATE POLICY "services: select" ON public.services
|
|
FOR SELECT TO authenticated
|
|
USING (
|
|
owner_id = auth.uid()
|
|
OR public.is_saas_admin()
|
|
OR tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
);
|
|
|
|
CREATE POLICY "services: insert" ON public.services
|
|
FOR INSERT TO authenticated
|
|
WITH CHECK (
|
|
owner_id = auth.uid()
|
|
AND tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
);
|
|
|
|
CREATE POLICY "services: update" ON public.services
|
|
FOR UPDATE TO authenticated
|
|
USING (owner_id = auth.uid() OR public.is_saas_admin())
|
|
WITH CHECK (owner_id = auth.uid() OR public.is_saas_admin());
|
|
|
|
CREATE POLICY "services: delete" ON public.services
|
|
FOR DELETE TO authenticated
|
|
USING (owner_id = auth.uid() OR public.is_saas_admin());
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#2 medicos — mesmo padrão
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS "medicos: owner full access" ON public.medicos;
|
|
DROP POLICY IF EXISTS "medicos: select" ON public.medicos;
|
|
DROP POLICY IF EXISTS "medicos: insert" ON public.medicos;
|
|
DROP POLICY IF EXISTS "medicos: update" ON public.medicos;
|
|
DROP POLICY IF EXISTS "medicos: delete" ON public.medicos;
|
|
|
|
CREATE POLICY "medicos: select" ON public.medicos
|
|
FOR SELECT TO authenticated
|
|
USING (
|
|
owner_id = auth.uid()
|
|
OR public.is_saas_admin()
|
|
OR tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
);
|
|
|
|
CREATE POLICY "medicos: insert" ON public.medicos
|
|
FOR INSERT TO authenticated
|
|
WITH CHECK (
|
|
owner_id = auth.uid()
|
|
AND tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
);
|
|
|
|
CREATE POLICY "medicos: update" ON public.medicos
|
|
FOR UPDATE TO authenticated
|
|
USING (owner_id = auth.uid() OR public.is_saas_admin())
|
|
WITH CHECK (owner_id = auth.uid() OR public.is_saas_admin());
|
|
|
|
CREATE POLICY "medicos: delete" ON public.medicos
|
|
FOR DELETE TO authenticated
|
|
USING (owner_id = auth.uid() OR public.is_saas_admin());
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#1 (parte 2) insurance_plans — mesmo padrão
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS "insurance_plans: owner full access" ON public.insurance_plans;
|
|
DROP POLICY IF EXISTS "insurance_plans: select" ON public.insurance_plans;
|
|
DROP POLICY IF EXISTS "insurance_plans: insert" ON public.insurance_plans;
|
|
DROP POLICY IF EXISTS "insurance_plans: update" ON public.insurance_plans;
|
|
DROP POLICY IF EXISTS "insurance_plans: delete" ON public.insurance_plans;
|
|
|
|
CREATE POLICY "insurance_plans: select" ON public.insurance_plans
|
|
FOR SELECT TO authenticated
|
|
USING (
|
|
owner_id = auth.uid()
|
|
OR public.is_saas_admin()
|
|
OR tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
);
|
|
|
|
CREATE POLICY "insurance_plans: insert" ON public.insurance_plans
|
|
FOR INSERT TO authenticated
|
|
WITH CHECK (
|
|
owner_id = auth.uid()
|
|
AND tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
);
|
|
|
|
CREATE POLICY "insurance_plans: update" ON public.insurance_plans
|
|
FOR UPDATE TO authenticated
|
|
USING (owner_id = auth.uid() OR public.is_saas_admin())
|
|
WITH CHECK (owner_id = auth.uid() OR public.is_saas_admin());
|
|
|
|
CREATE POLICY "insurance_plans: delete" ON public.insurance_plans
|
|
FOR DELETE TO authenticated
|
|
USING (owner_id = auth.uid() OR public.is_saas_admin());
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#3 commitment_services — cascade via JOIN com services.tenant_id
|
|
-- (tabela N:N sem tenant_id próprio; herda do services pai)
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS "commitment_services: owner full access" ON public.commitment_services;
|
|
DROP POLICY IF EXISTS "commitment_services: tenant_member" ON public.commitment_services;
|
|
|
|
CREATE POLICY "commitment_services: tenant_member" ON public.commitment_services
|
|
FOR ALL TO authenticated
|
|
USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.services s
|
|
WHERE s.id = commitment_services.service_id
|
|
AND (
|
|
s.owner_id = auth.uid()
|
|
OR public.is_saas_admin()
|
|
OR s.tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
)
|
|
)
|
|
)
|
|
WITH CHECK (
|
|
EXISTS (
|
|
SELECT 1 FROM public.services s
|
|
WHERE s.id = commitment_services.service_id
|
|
AND (s.owner_id = auth.uid() OR public.is_saas_admin())
|
|
)
|
|
);
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#4 insurance_plan_services — cascade via JOIN com insurance_plans
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS "insurance_plan_services_owner" ON public.insurance_plan_services;
|
|
DROP POLICY IF EXISTS "insurance_plan_services: tenant_member" ON public.insurance_plan_services;
|
|
|
|
CREATE POLICY "insurance_plan_services: tenant_member" ON public.insurance_plan_services
|
|
FOR ALL TO authenticated
|
|
USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.insurance_plans ip
|
|
WHERE ip.id = insurance_plan_services.insurance_plan_id
|
|
AND (
|
|
ip.owner_id = auth.uid()
|
|
OR public.is_saas_admin()
|
|
OR ip.tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
)
|
|
)
|
|
)
|
|
WITH CHECK (
|
|
EXISTS (
|
|
SELECT 1 FROM public.insurance_plans ip
|
|
WHERE ip.id = insurance_plan_services.insurance_plan_id
|
|
AND (ip.owner_id = auth.uid() OR public.is_saas_admin())
|
|
)
|
|
);
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#5 — adicionar WITH CHECK em INSERT das 3 tabelas que não tinham
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS ctl_insert_for_active_member ON public.commitment_time_logs;
|
|
CREATE POLICY ctl_insert_for_active_member ON public.commitment_time_logs
|
|
FOR INSERT TO authenticated
|
|
WITH CHECK (
|
|
tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
);
|
|
|
|
DROP POLICY IF EXISTS dcf_insert_for_active_member ON public.determined_commitment_fields;
|
|
CREATE POLICY dcf_insert_for_active_member ON public.determined_commitment_fields
|
|
FOR INSERT TO authenticated
|
|
WITH CHECK (
|
|
tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
);
|
|
|
|
DROP POLICY IF EXISTS dc_insert_for_active_member ON public.determined_commitments;
|
|
CREATE POLICY dc_insert_for_active_member ON public.determined_commitments
|
|
FOR INSERT TO authenticated
|
|
WITH CHECK (
|
|
tenant_id IN (
|
|
SELECT tm.tenant_id FROM public.tenant_members tm
|
|
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
|
|
)
|
|
);
|