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>
128 lines
6.0 KiB
SQL
128 lines
6.0 KiB
SQL
-- =============================================================================
|
|
-- Migration: 20260419000015_communication_security_hardening
|
|
-- Sessão 6 — revisão Comunicação. Resolve V#1-V#5 (2 críticos + 3 altos).
|
|
-- V#6-V#10 adiados (médios/baixos com plano completo no DB).
|
|
--
|
|
-- 🔴 V#1+V#2 são bugs P0: policies usavam (tenant_id = auth.uid()) — comparação
|
|
-- de UUID de tenant com UUID de user. Tabelas inacessíveis na prática.
|
|
-- =============================================================================
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#1: email_layout_config — fix BUG do tenant_id = auth.uid()
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS "tenant owns email layout config" ON public.email_layout_config;
|
|
DROP POLICY IF EXISTS "email_layout_config: tenant_admin all" ON public.email_layout_config;
|
|
|
|
CREATE POLICY "email_layout_config: tenant_admin all" ON public.email_layout_config
|
|
FOR ALL TO authenticated
|
|
USING (
|
|
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'
|
|
AND tm.role IN ('tenant_admin','admin','owner')
|
|
)
|
|
)
|
|
WITH CHECK (
|
|
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'
|
|
AND tm.role IN ('tenant_admin','admin','owner')
|
|
)
|
|
);
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#2: email_templates_tenant — MESMO bug
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS "tenant manages own overrides" ON public.email_templates_tenant;
|
|
DROP POLICY IF EXISTS "email_templates_tenant: tenant_admin all" ON public.email_templates_tenant;
|
|
|
|
CREATE POLICY "email_templates_tenant: tenant_admin all" ON public.email_templates_tenant
|
|
FOR ALL TO authenticated
|
|
USING (
|
|
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'
|
|
AND tm.role IN ('tenant_admin','admin','owner')
|
|
)
|
|
)
|
|
WITH CHECK (
|
|
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'
|
|
AND tm.role IN ('tenant_admin','admin','owner')
|
|
)
|
|
);
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#3: notification_logs — SELECT pra tenant_member
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS "notif_logs_tenant_member" ON public.notification_logs;
|
|
CREATE POLICY "notif_logs_tenant_member" ON public.notification_logs
|
|
FOR SELECT TO authenticated
|
|
USING (
|
|
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'
|
|
)
|
|
);
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#4: notification_queue — SELECT pra tenant_member
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS "notif_queue_tenant_member" ON public.notification_queue;
|
|
CREATE POLICY "notif_queue_tenant_member" ON public.notification_queue
|
|
FOR SELECT TO authenticated
|
|
USING (
|
|
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'
|
|
)
|
|
);
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- V#5: notification_channels — SELECT pra tenant_member; INSERT tenant_admin; UPDATE/DELETE owner
|
|
-- -----------------------------------------------------------------------------
|
|
DROP POLICY IF EXISTS "notification_channels_owner" ON public.notification_channels;
|
|
DROP POLICY IF EXISTS "notif_channels_select" ON public.notification_channels;
|
|
DROP POLICY IF EXISTS "notif_channels_insert" ON public.notification_channels;
|
|
DROP POLICY IF EXISTS "notif_channels_modify" ON public.notification_channels;
|
|
|
|
CREATE POLICY "notif_channels_select" ON public.notification_channels
|
|
FOR SELECT TO authenticated
|
|
USING (
|
|
deleted_at IS NULL
|
|
AND (
|
|
public.is_saas_admin()
|
|
OR owner_id = auth.uid()
|
|
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 "notif_channels_insert" ON public.notification_channels
|
|
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 "notif_channels_modify" ON public.notification_channels
|
|
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 "notif_channels_delete" ON public.notification_channels
|
|
FOR DELETE TO authenticated
|
|
USING (owner_id = auth.uid() OR public.is_saas_admin());
|