10 KiB
Feature Specification: Desk Auth & RBAC (003)
Feature Branch: 003-desk-auth-rbac
Created: 2026-06-10
Status: Draft → Ready for plan
Input: Proteger o Ligbox Ops Desk (API pública em api.ops.ligbox.com.br) com login de utilizadores e controlo de acesso por perfil (RBAC). Utilizadores iniciais mapeados aos accounts Linux da VM122: root, admin, mini, mais noc para monitorização.
Backlog: OPS-4 (auth), OPS-6 (RBAC), DESK-1/2 (Desk protegido)
User Scenarios & Testing (mandatory)
User Story 1 — Login no Support Desk (Priority: P1)
Como membro da equipa Ligbox, quero autenticar-me no Desk com utilizador e senha para aceder a tickets e dashboards sem que dados de clientes fiquem expostos na internet.
Why this priority: Hoje qualquer pessoa com o URL lê tickets, CNPJs e perfis de empresa. É o maior risco de segurança e privacidade da plataforma.
Independent Test: Abrir https://desk.ligbox.com.br sem sessão → ver ecrã de login. Após credenciais válidas → dashboard carrega. curl sem token em /api/v1/desk/tickets → HTTP 401.
Acceptance Scenarios:
- Given utilizador não autenticado, When abre o Desk UI, Then vê formulário de login (não o dashboard).
- Given credenciais válidas (
root/ senha correcta), When submete login, Then recebe token de sessão e acede ao dashboard. - Given credenciais inválidas, When submete login, Then recebe erro genérico sem revelar se o utilizador existe.
- Given token expirado, When UI chama API protegida, Then redirecciona para login.
- Given pedido API sem
Authorizationnem webhook secret, When acede endpoint humano (/api/v1/desk/*), Then HTTP 401.
User Story 2 — Perfis e permissões (Priority: P1)
Como administrador, quero que cada técnico veja e faça apenas o que o seu perfil permite — Roger com controlo total, chefe de ops com gestão operacional, suporte com tickets, NOC só leitura.
Why this priority: Autenticação sem autorização não resolve o problema; roles definem o modelo operacional da equipa.
Independent Test: Login como mini (technician) → pode ver tickets e fechar os atribuídos. Login como noc → vê dashboard e alertas Wazuh mas botão "Fechar ticket" ausente e PATCH retorna 403.
Acceptance Scenarios:
- Given
root(super_admin), When acede qualquer endpoint humano, Then permitido (incluindo gestão de utilizadores). - Given
admin(ops_lead), When tenta criar utilizador, Then HTTP 403; When fecha ticket ou dispara audit, Then permitido. - Given
mini(technician), When consulta tickets, Then vê lista completa; When altera ticket não atribuído a si, Then HTTP 403 (excepto tickets sem assignee — pode assumir). - Given
noc, When consulta dashboard/health/Wazuh, Then permitido em leitura; When tenta PATCH ticket ou POST audit, Then HTTP 403. - Given
noc, When vê ticket comcompany_profile, Then campos sensíveis (CNPJ, morada, emails billing) mascarados na resposta API e UI.
User Story 3 — Webhooks e integrações intactos (Priority: P1)
Como sistema integrado (VM112, Wazuh), continuo a enviar eventos via secret sem passar pelo login humano — a auth RBAC não pode quebrar o funil de onboarding.
Why this priority: A plataforma já recebe eventos de produção; regressão aqui bloqueia novos clientes.
Independent Test: POST /api/v1/webhooks/onboard com X-Webhook-Secret válido sem JWT → HTTP 200. Com secret inválido → 401.
Acceptance Scenarios:
- Given header
X-Webhook-Secretválido, When POST webhook onboard ou wazuh, Then processado sem JWT. - Given JWT válido mas sem secret, When POST webhook, Then HTTP 401 (webhooks não aceitam JWT como substituto).
- Given
GET /health, When sem auth, Then HTTP 200 (healthcheck Traefik/monitoring).
User Story 4 — Gestão de utilizadores (Priority: P2)
Como super_admin (Roger), quero listar utilizadores do Desk, activar/desactivar contas e alterar roles sem SSH na VM.
Why this priority: Operação diária; não bloqueia MVP se seeds iniciais bastarem no lançamento.
Independent Test: Login root → GET /api/v1/auth/users lista 4 users. PATCH role de mini → technician mantido; tentativa por admin → 403.
Acceptance Scenarios:
- Given super_admin autenticado, When lista utilizadores, Then vê username, role, activo, último login (sem password hash).
- Given super_admin, When desactiva utilizador
noc, Then login desse user falha no próximo pedido. - Given ops_lead ou inferior, When acede
/api/v1/auth/users, Then HTTP 403.
Utilizadores iniciais (seed VM122)
| Username | Role | Perfil operacional | Senha inicial |
|---|---|---|---|
root |
super_admin |
Roger / dono — tudo: users, tenants, audit, tickets, config | 805353 (bootstrap; rotacionar em produção) |
admin |
ops_lead |
Chefe de operações — tickets, audit, fechar casos, funil completo | 805353 |
mini |
technician |
Suporte N1/N2 — tickets atribuídos, timeline, acções limitadas | 805353 |
noc |
noc |
Monitorização — só leitura: dashboard, Wazuh, health | 805353 |
Nota: Accounts Linux (
root,admin,mini) já existem na VM122 com sudo. O utilizadornocé criado no seed do Desk (conta só na app, não requer user OS). Passwords Desk são independentes do OS após seed — alteração no Desk não muda SSH.
Matriz de permissões (RBAC)
Legenda: ✅ permitido · 🔒 leitura restrita (dados mascarados) · ❌ negado
| Recurso / Acção | super_admin | ops_lead | technician | noc |
|---|---|---|---|---|
| Login / logout | ✅ | ✅ | ✅ | ✅ |
GET /health |
✅ público | ✅ | ✅ | ✅ |
Webhooks (POST /webhooks/*) |
secret | secret | secret | secret |
| Dashboard summary | ✅ | ✅ | ✅ | 🔒 |
| Listar tickets | ✅ | ✅ | ✅ | 🔒 |
| Ver detalhe ticket | ✅ | ✅ | ✅ | 🔒 |
| Fechar / reabrir ticket | ✅ | ✅ | ✅* | ❌ |
Atribuir ticket (assigned_to) |
✅ | ✅ | ✅** | ❌ |
| Funil onboarding (completo) | ✅ | ✅ | parcial | 🔒 resumo |
| Timeline sessão | ✅ | ✅ | ✅ | 🔒 |
| Audit overview / scorecard | ✅ | ✅ | ❌ | 🔒 |
| Disparar audit manual | ✅ | ✅ | ❌ | ❌ |
| Listar tenants | ✅ | ✅ | ✅ | 🔒 |
| Eventos webhook (todos) | ✅ | ✅ | onboard+wazuh | wazuh only |
| Infra status (VM112, Wazuh) | ✅ | ✅ | ✅ | ✅ |
| Gestão utilizadores | ✅ | ❌ | ❌ | ❌ |
Ver company_profile completo |
✅ | ✅ | ✅ | ❌ mascarado |
Ver billing_state |
✅ | ✅ | ✅ | ❌ |
* technician: PATCH apenas se assigned_to = self OU assigned_to IS NULL (pode assumir ao fechar).
** technician: pode atribuir a si próprio; ops_lead+ pode atribuir a qualquer user.
Functional Requirements
- FR-001: Sistema MUST exigir autenticação (JWT Bearer) em todos os endpoints humanos sob
/api/v1/excepto/health,/api/health, e webhooks. - FR-002: Sistema MUST validar role em cada endpoint conforme matriz RBAC.
- FR-003: Sistema MUST armazenar passwords com hash bcrypt (cost ≥ 12); nunca plaintext.
- FR-004: Sistema MUST emitir JWT com claims:
sub(username),role,exp(TTL configurável, default 8h). - FR-005: Sistema MUST manter canal paralelo de auth para webhooks via
X-Webhook-Secret(inalterado). - FR-006: UI MUST apresentar login antes de qualquer vista; MUST enviar
Authorization: Bearer <token>em pedidos API. - FR-007: UI MUST ocultar acções não permitidas ao role (ex.: noc sem botão fechar ticket).
- FR-008: Sistema MUST mascarar campos sensíveis em respostas para role
noc(tax_id, morada, emails billing). - FR-009: Sistema MUST seed 4 utilizadores na primeira execução se tabela
desk_usersvazia. - FR-010: super_admin MUST poder listar, activar/desactivar users e alterar roles (P2).
- FR-011: Sistema MUST adicionar
assigned_to(nullable) em tickets para controlo technician (P2 mínimo: campo + PATCH por ops_lead). - FR-012: Sistema MUST registar
last_login_atpor utilizador. - FR-013: Sistema MUST falhar de forma segura: 401 sem token, 403 com token mas sem permissão.
Success Criteria
- SC-001: 100% dos endpoints humanos devolvem 401 sem autenticação (verificado por script
verify-auth.sh). - SC-002: Nenhum dado de ticket acessível publicamente em
api.ops.ligbox.com.brapós deploy. - SC-003: Webhooks VM112 e Wazuh continuam a funcionar sem alteração de secret.
- SC-004: Cada um dos 4 roles passa testes de permissão documentados em quickstart.
- SC-005: Tempo de login < 2s p95 na LAN.
Edge Cases
- Token expirado durante sessão activa → UI pede re-login; não perde navegação abrupta sem mensagem.
- Utilizador desactivado com token válido → próximo pedido API retorna 401.
- Traefik healthcheck usa
/health→ permanece público. - Worker interno chama API (audit cycle) → usa
OPS_INTERNAL_TOKENou chama localhost sem auth (decisão no plan). - Brute force login → rate limit 5 tentativas/min por IP (Redis ou in-memory MVP).
- CORS: frontend e API no mesmo origin via Traefik (
desk.ligbox.com.br/apiproxy) — validar no deploy.
Assumptions
- VM122 Debian 12, API FastAPI existente, SQLite
ops.db. - Frontend estático servido por nginx; sem framework JS pesado.
- Traefik CT114 termina TLS e expõe
desk.ligbox.com.breapi.ops.ligbox.com.br. - Senha bootstrap
805353será rotacionada após primeiro login super_admin (processo manual documentado). - Assignment de tickets (FR-011) pode entrar na mesma entrega se simples; caso contrário fase 2 da 003.
Out of Scope
- SSO / OAuth externo (Google, Azure AD)
- MFA / TOTP no Desk (portal onboard já tem; Desk fica para spec futura)
- Permissões por tenant (todos os roles Ligbox vêem todos os tenants no MVP)
- Audit log de acções de utilizador (spec futura compliance)
- Sincronização automática password Desk ↔ Linux PAM
Dependencies
- Features 001 (webhooks) e 002 (Wazuh) — deployadas e funcionais.
python-joseouPyJWT+passlib[bcrypt]no requirements API.- Redis existente (rate limit opcional).