prompt inicial do projeto completo

This commit is contained in:
2026-07-22 18:45:09 -03:00
parent 6fb920d333
commit b9ede753c4
12 changed files with 64 additions and 18 deletions

View File

@@ -40,7 +40,7 @@ Um controller `GET /api/v1/health` devolvendo `{"status":"UP"}`, só para provar
### 6. `infra/`
`docker-compose.yml` exatamente como em `docs/05-INFRA-DOCKER.md` §2, mais `docker-compose.observability.yml` e `.env.example`. Configs em `livekit/livekit.yaml`, `traefik/`, `postgres/init/01-extensions.sql` (`CREATE EXTENSION postgis; CREATE EXTENSION pgcrypto;`).
`docker-compose.yml` exatamente como em `docs/05-INFRA-DOCKER.md` §2, mais `docker-compose.observability.yml` e `.env.example`. Configs em `livekit/livekit.yaml`, `traefik/`, `postgres/init/01-extensions.sql` (`CREATE EXTENSION postgis; CREATE EXTENSION pgcrypto;`) e `postgres/init/02-roles.sh` (cria o papel `portaria_app`, sem ownership, com a senha de `POSTGRES_APP_PASSWORD` — ver `02-MODELO-DE-DADOS.md` §8).
### 7. CI — GitHub Actions

View File

@@ -73,7 +73,9 @@ Filtro/interceptor que lê `Idempotency-Key`, consulta `idempotency_keys`, e:
Spring Security com três cadeias de filtro conforme `06-LGPD-E-SEGURANCA.md` §6: `/api/v1/visitor/**` (JWT efêmero), `/api/v1/app/**` (OIDC), `/api/v1/admin/**` (OIDC + MFA).
Emissão e validação do JWT de visitante: 15 min, escopo de uma visita, atado a `gateId` e IP.
Emissão e validação do JWT de visitante: 15 min, escopo de uma visita, atado a `gateId` e à sessão — **não ao IP**: IP de celular muda no meio da sessão (CGNAT, troca de torre) e derrubaria visitante legítimo.
**Conexões ao banco:** Flyway como `portaria` (dono), aplicação como `portaria_app` — sem essa separação, o revoke do `audit_log` e a RLS futura são decorativos (dono ignora ambos). Ver `02-MODELO-DE-DADOS.md` §8.
### 8. Auditoria

View File

@@ -27,6 +27,7 @@ Localização ausente ou negada → `403 LOCATION_REQUIRED`. Fora do raio → `4
- Resolve `unit_id` a partir de bloco + unidade; **se não existir, cria a visita com `unit_id = NULL`** e preserva `unit_input`
- Resposta **idêntica** — mesmo corpo, mesmo status, mesmos headers — nos dois casos
- **Tempo de resposta constante:** meça o caminho mais lento e aplique delay artificial no mais rápido. Sem isso o ataque vira timing attack e a busca cega não serve para nada
- **Visita-sombra:** com `unit_id` NULL — ou unidade sem nenhum dispositivo ativo — os jobs percorrem a **mesma linha do tempo de estados**, nos mesmos tempos, sem notificar ninguém; com `operator_queue` ativo, entra na fila marcada "unidade não cadastrada" (`03-FLUXOS-E-CONTRATOS.md` §2). Sem isso, a sequência de estados no WebSocket entrega o que o tempo constante escondeu
- Nome de morador nunca aparece na resposta
- Rate limit por IP e por dispositivo, com bloqueio progressivo
@@ -91,6 +92,7 @@ Importação CSV de unidades com **dry-run obrigatório** — devolve o que seri
- [ ] Testes de integração com Testcontainers cobrindo os fluxos A, B e C ponta a ponta
- [ ] **Teste de busca cega:** unidade existente e inexistente produzem resposta idêntica, e a diferença de tempo fica abaixo do ruído de medição
- [ ] **Teste de visita-sombra:** unidade inexistente percorre a mesma sequência de estados, nos mesmos tempos, que uma visita real não atendida — e ninguém é notificado
- [ ] Teste: geofence rejeita fora do raio e aceita dentro
- [ ] Teste: `quiet_hours` desvia do morador
- [ ] Teste: dois `resolve` concorrentes — um vence, outro recebe `409` com o autor

View File

@@ -22,7 +22,7 @@ Login OIDC com **MFA obrigatório** — este perfil vê fotos de todos os visita
### 2. Auditoria de visitas — a tela mais importante
Tabela virtualizada com filtros persistidos na URL: período, condomínio, bloco, unidade, tipo, estado, **dentro/fora do geofence**, e quem resolveu.
Tabela virtualizada com filtros persistidos na URL: período, condomínio, bloco, unidade, tipo, estado, **dentro/fora do geofence**, quem resolveu, e **"unidade não cadastrada"** (visitas-sombra: erro de digitação legítimo ou tentativa de enumeração — recorrência do mesmo dispositivo em unidades diferentes é padrão de varredura e deve gerar destaque).
Colunas: data/hora, tipo, visitante, unidade, estado, quem resolveu, duração, selo de geofence.

View File

@@ -40,6 +40,8 @@ Mesmo binário Compose Multiplatform, perfil decidido por `GET /me`. Layout mais
**Contexto** à direita, antes de assumir: histórico da unidade, visitas recentes, regra de entrega, nomes dos moradores, telefone. **O operador nunca atende sem contexto** — atender às cegas é o que faz a portaria remota parecer pior que a física.
**Visitas-sombra** (`unit_id` nulo — busca cega, `03-FLUXOS-E-CONTRATOS.md` §2) chegam à fila marcadas como **"unidade não cadastrada"**, visível só para o operador. Ele trata como um porteiro trataria quem errou o número: pergunta, corrige a unidade (`PATCH` que resolve o `unit_id` e registra em `audit_log`) e redireciona — ou nega. Para o visitante, nada distingue esse atendimento de um normal.
Presença: `DISPONIVEL` / `EM_ATENDIMENTO` / `OFFLINE`, com heartbeat. Operador que perde conexão volta para `OFFLINE` automaticamente e suas visitas retornam à fila.
Ao resolver, o operador registra `resolution_reason` — obrigatório, e é o que alimenta a análise de por que o modelo autônomo falhou naquele caso.

View File

@@ -18,7 +18,7 @@ Playwright para os webs, Maestro para os apps. Cenários obrigatórios:
2. Entrega em um toque, resolvida pela notificação, em menos de 10s
3. Escalonamento completo: ninguém atende → outros moradores → fila → operador → autorizado
4. Recado: ninguém atende, sem módulo de operador → grava → morador resolve depois
5. Unidade inexistente: visita criada, ninguém notificado, expira — **resposta indistinguível de unidade válida**
5. Unidade inexistente: visita criada, ninguém notificado, e a **sequência de estados vista pelo visitante é idêntica** à de uma visita real não atendida (visita-sombra) — incluindo os tempos das transições
6. Fora do geofence: bloqueado com mensagem acionável
7. Quiet hours: desvia sem tocar em ninguém
8. Dois moradores respondendo juntos: um vence, o outro vê quem resolveu
@@ -56,7 +56,7 @@ Derrube cada peça, uma por vez, com uma visita em andamento, e verifique o comp
Percorra o modelo de ameaças de `06-LGPD-E-SEGURANCA.md` §5 e prove cada mitigação:
- [ ] **A.1 Busca cega** — tempo de resposta constante medido estatisticamente, não no olho. Este é o teste mais importante da fase
- [ ] **A.1 Busca cega** — tempo de resposta constante medido estatisticamente, não no olho, **e** linha do tempo de estados idêntica entre unidade real não atendida e visita-sombra. Este é o teste mais importante da fase
- [ ] **A.2 Geofence** — rejeita fora do raio; GPS falsificado é registrado
- [ ] **A.3 DoS social** — rate limit e quiet hours funcionam; varredura gera alerta
- [ ] **A.4 Mídia** — nenhuma URL pública; assinada expira; acesso auditado