obsidian-vault/ligbox-ops-platform/specs/003-desk-auth-rbac/research.md
2026-06-19 17:26:42 +00:00

145 lines
3.7 KiB
Markdown

# Research: Desk Auth & RBAC (003)
**Date**: 2026-06-10
**Feature**: 003-desk-auth-rbac
---
## R1 — Estado actual da API (exposição pública)
**Decisão**: Auth JWT obrigatório em endpoints humanos.
**Evidência** (2026-06-10):
```bash
curl -sf https://api.ops.ligbox.com.br/api/v1/desk/tickets
# → 200, 11 tickets com company_profile/CNPJ sem auth
```
Endpoints sem protecção em `api/app/main.py`:
- 14 rotas GET/PATCH humanas
- 2 rotas POST webhook (protegidas por secret ✅)
- 2 rotas health (devem ficar públicas ✅)
**Alternativas rejeitadas**:
- Basic Auth no Traefik → não dá RBAC granular por endpoint
- VPN only → desk precisa ser acessível à equipa remota
- API key única partilhada → não distingue roles
---
## R2 — Utilizadores VM122
**Decisão**: Mapear usernames Linux existentes para Desk; criar `noc` só na app.
**Evidência**:
| OS user | Existe VM122 | Role Desk |
|---------|--------------|-----------|
| root | ✅ sudo | super_admin |
| admin | ✅ sudo | ops_lead |
| mini | ✅ sudo | technician |
| noc | ❌ criar seed app | noc |
Passwords OS bootstrap `805353` — usar mesma senha no seed Desk com obrigação de rotação documentada.
**Alternativas rejeitadas**:
- PAM/Linux auth directo → container Docker não vê `/etc/shadow` facilmente; acoplamento desnecessário
- Emails como username → equipa já usa root/admin/mini no SSH
---
## R3 — JWT vs session cookie
**Decisão**: JWT Bearer em `Authorization` header; token em `sessionStorage` no frontend.
**Motivo**:
- Frontend é nginx static files sem backend de sessão
- API já é stateless FastAPI
- Traefik pode servir desk + api no mesmo origin
**TTL**: 8 horas (turno de trabalho); refresh token fora de scope MVP.
**Alternativas rejeitadas**:
- HttpOnly cookie → requer same-site config Traefik + CSRF; mais complexo
- SQLite sessions → estado no servidor; YAGNI
---
## R4 — Worker interno (audit cycle)
**Decisão**: Header `X-Ops-Internal-Token` para `POST /api/v1/audit/cycle`.
**Evidência**: `worker/audit_runner.py` chama API periodicamente sem utilizador humano.
**Alternativas rejeitadas**:
- Service account JWT com expiry longo → rotação mais complexa
- Worker escreve SQLite directo → viola separação container
---
## R5 — Mascaramento NOC
**Decisão**: Server-side mask em `_enrich_ticket()` quando `role == noc`.
**Campos mascarados**:
- `company_profile.tax_id``***`
- `company_profile.address` → omitido
- `email_billing`, `email_legal``***@***`
- `billing_state` → omitido
UI não é única linha de defesa — API também mascara.
---
## R6 — Technician assignment
**Decisão**: Coluna `assigned_to TEXT` em `tickets`; PATCH permitido se assignee match ou null.
**MVP**: ops_lead atribui via `PATCH` body `{status, assigned_to}`; technician pode self-assign ao abrir ticket.
**Alternativas rejeitadas**:
- Fila separada por user → over-engineering para 11 tickets actuais
- Technician vê só assigned → demasiado restritivo sem processo de triagem definido
---
## R7 — Rate limiting login
**Decisão**: In-memory dict `{ip: [timestamps]}` no processo API para MVP; migrar Redis se multi-worker.
**Limite**: 5 tentativas / 60s / IP → HTTP 429.
**Alternativas rejeitadas**:
- fail2ban no login HTTP → fail2ban é SSH-focused; complementar depois
---
## R8 — Feature flag rollback
**Decisão**: `DESK_AUTH_ENABLED=false` desactiva verificação JWT (emergência).
Default `true` após implementação completa e verify-auth.sh verde.
---
## R9 — Dependências Python
**Decisão**:
```
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
bcrypt==4.2.1
```
`passlib` + `bcrypt` compatíveis com Python 3.11 no container Debian.