107 lines
4.5 KiB
Markdown
107 lines
4.5 KiB
Markdown
|
|
---
|
||
|
|
adr: ADR-004
|
||
|
|
title: Auth model evolution — manter Supabase GoTrue + RLS publico-read; novas tabelas case/* write-only por investigator role
|
||
|
|
status: accepted
|
||
|
|
date: 2026-05-23
|
||
|
|
deciders: sa-principal, sa-security-engineer, sa-platform-lead
|
||
|
|
project: disclosure-bureau
|
||
|
|
---
|
||
|
|
|
||
|
|
## Context
|
||
|
|
|
||
|
|
Modelo de auth atual:
|
||
|
|
|
||
|
|
- **GoTrue email magic-link**, SMTP Spacemail, `MAILER_AUTOCONFIRM=false`.
|
||
|
|
- **profiles.role ∈ {user, admin, suspended}**, com `budget_cap_usd`, `daily_quota`.
|
||
|
|
- **Sessoes `is_public=true`** sao readable por anon (compartilhaveis via share_token UUID).
|
||
|
|
- **RLS publico-read** em chunks/entities/documents/entity_mentions/relations.
|
||
|
|
- **service_role key em env do container web** (necessario para usage_events INSERT bypass RLS + admin tasks). F5 do security audit.
|
||
|
|
|
||
|
|
Decisao de fronteira na W3: novas tabelas (`hypotheses`, `evidence`, `contradictions`, `witnesses`, `gaps`, `residual_uncertainties`, `investigation_jobs`) — quem escreve?
|
||
|
|
|
||
|
|
## Options
|
||
|
|
|
||
|
|
1. **service_role escreve tudo** (mesmo padrao atual). Investigator-runtime usa service_role.
|
||
|
|
2. **Role intermediario `investigator`** com permissoes minimas. Investigator-runtime usa esse role; web nao.
|
||
|
|
3. **Investigator usa Postgres direto sem RLS**: bypass desde o nivel de conexao.
|
||
|
|
|
||
|
|
## Decision
|
||
|
|
|
||
|
|
**Opcao 2: Role `investigator` granular.**
|
||
|
|
|
||
|
|
Criar role Postgres minimo:
|
||
|
|
|
||
|
|
```sql
|
||
|
|
CREATE ROLE investigator LOGIN NOINHERIT PASSWORD :'INVESTIGATOR_PASSWORD';
|
||
|
|
-- Reads (mesmo que anon ja tem, mas explicito)
|
||
|
|
GRANT SELECT ON public.chunks, public.entities, public.entity_mentions,
|
||
|
|
public.relations, public.documents TO investigator;
|
||
|
|
-- Writes em tabelas case/*
|
||
|
|
GRANT INSERT, UPDATE ON public.hypotheses, public.evidence, public.contradictions,
|
||
|
|
public.witnesses, public.gaps, public.residual_uncertainties,
|
||
|
|
public.investigation_jobs TO investigator;
|
||
|
|
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO investigator;
|
||
|
|
-- Negar tudo o resto
|
||
|
|
REVOKE ALL ON public.profiles, public.chat_sessions, public.messages,
|
||
|
|
public.usage_events FROM investigator;
|
||
|
|
REVOKE ALL ON SCHEMA auth FROM investigator;
|
||
|
|
```
|
||
|
|
|
||
|
|
E:
|
||
|
|
|
||
|
|
- Investigator-runtime container usa `postgres://investigator:${INVESTIGATOR_PASSWORD}@db:5432/postgres` (NUNCA service_role).
|
||
|
|
- Web service continua com `postgres://postgres:...` (acesso amplo necessario para createServiceClient nos pontos especificos).
|
||
|
|
- Em W5, **avaliar reducao de uso de service_role no web** criando role `web_service` similar com permissoes ainda menores que `postgres`.
|
||
|
|
|
||
|
|
**RLS nas novas tabelas:**
|
||
|
|
|
||
|
|
```sql
|
||
|
|
ALTER TABLE public.hypotheses ENABLE ROW LEVEL SECURITY;
|
||
|
|
CREATE POLICY hypotheses_public_read ON public.hypotheses FOR SELECT USING (TRUE);
|
||
|
|
GRANT SELECT ON public.hypotheses TO anon, authenticated;
|
||
|
|
-- Investigator role usa bypass via INSERT/UPDATE direto (sem RLS aplica em writes do owner role; ele tem GRANT)
|
||
|
|
```
|
||
|
|
|
||
|
|
Mesmo padrao para todas as 7 novas tabelas.
|
||
|
|
|
||
|
|
**Modificacoes nas existentes:**
|
||
|
|
|
||
|
|
- `relations` ganha RLS (F4). Hoje sem.
|
||
|
|
- `messages.citations JSONB` continua mas adicionar coluna `hypothesis_id REFERENCES public.hypotheses(hypothesis_id)` se chat citar hipoteses geradas.
|
||
|
|
|
||
|
|
**Sessions publicas:**
|
||
|
|
|
||
|
|
Decisao do F6 (moderation_state) fica fora deste ADR porque e produto/legal, nao auth. Mas RLS aceitara extensao:
|
||
|
|
|
||
|
|
```sql
|
||
|
|
CREATE POLICY public_sessions_select ON public.chat_sessions
|
||
|
|
FOR SELECT USING (
|
||
|
|
auth.uid() = user_id OR
|
||
|
|
(is_public = TRUE AND COALESCE(moderation_state, 'approved') IN ('approved'))
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
## Consequences
|
||
|
|
|
||
|
|
**Positivas:**
|
||
|
|
- Blast radius do investigator-runtime menor (RCE no container nao acesso ao auth.users / profiles).
|
||
|
|
- Auditabilidade granular: cada INSERT em hypotheses/evidence tem `created_by = '<detective>@detective'` + role Postgres `investigator`.
|
||
|
|
- Aderencia a least-privilege.
|
||
|
|
|
||
|
|
**Negativas:**
|
||
|
|
- 1 password adicional para gerir (`INVESTIGATOR_PASSWORD`). Mitigacao: docker secrets em W5.
|
||
|
|
- Investigator nao pode ler `messages` (intencional) — se algum dia detetive precisar contexto de sessao do user, precisa de hand-off explicito (ex: chat passar `userTurn` no `payload` do job).
|
||
|
|
|
||
|
|
## Verification
|
||
|
|
|
||
|
|
- `\du investigator` confirma role + permissoes.
|
||
|
|
- Teste manual: investigator role tenta `SELECT FROM auth.users` -> erro permission denied.
|
||
|
|
- Teste manual: investigator role faz `INSERT INTO hypotheses` -> sucesso.
|
||
|
|
- Service_role uses count auditavel: grep `createServiceClient` no `web/`.
|
||
|
|
|
||
|
|
## References
|
||
|
|
|
||
|
|
- `security-audit-report.md` F4, F5, F6
|
||
|
|
- `agentic-layer-spec.md` secao 3.2 (container env)
|
||
|
|
- `infra/disclosure-stack/init-db.sql` (roles bootstrap atual)
|