172 lines
3.3 KiB
Markdown
172 lines
3.3 KiB
Markdown
# API Contract: Desk Auth & RBAC
|
|
|
|
**Service**: Ligbox Ops Platform API (VM122)
|
|
**Base URL (LAN)**: `http://10.10.10.122:8080`
|
|
**Base URL (público)**: `https://api.ops.ligbox.com.br`
|
|
**Human Auth**: `Authorization: Bearer <JWT>`
|
|
**Machine Auth**: `X-Webhook-Secret` (webhooks only)
|
|
**Internal Auth**: `X-Ops-Internal-Token` (worker audit cycle)
|
|
|
|
---
|
|
|
|
## POST /api/v1/auth/login
|
|
|
|
Público. Não requer JWT.
|
|
|
|
```http
|
|
POST /api/v1/auth/login HTTP/1.1
|
|
Content-Type: application/json
|
|
|
|
```
|
|
|
|
### Response 200
|
|
|
|
```json
|
|
{
|
|
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
"token_type": "bearer",
|
|
"expires_in": 28800,
|
|
"username": "root",
|
|
"role": "super_admin",
|
|
"display_name": "Roger"
|
|
}
|
|
```
|
|
|
|
### Response 401
|
|
|
|
```json
|
|
{ "detail": "invalid credentials" }
|
|
```
|
|
|
|
### Response 429
|
|
|
|
```json
|
|
{ "detail": "too many login attempts" }
|
|
```
|
|
|
|
---
|
|
|
|
## GET /api/v1/auth/me
|
|
|
|
```http
|
|
GET /api/v1/auth/me HTTP/1.1
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
### Response 200
|
|
|
|
```json
|
|
{
|
|
"username": "mini",
|
|
"role": "technician",
|
|
"display_name": "Suporte",
|
|
"active": true,
|
|
"last_login_at": "2026-06-10T12:00:00+00:00"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## GET /api/v1/desk/tickets (protegido)
|
|
|
|
```http
|
|
GET /api/v1/desk/tickets HTTP/1.1
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
### Sem token → 401
|
|
|
|
```json
|
|
{ "detail": "not authenticated" }
|
|
```
|
|
|
|
### noc → 200 com dados mascarados
|
|
|
|
`company_profile.tax_id` e morada omitidos/mascarados.
|
|
|
|
---
|
|
|
|
## PATCH /api/v1/desk/tickets/{id}
|
|
|
|
```http
|
|
PATCH /api/v1/desk/tickets/11 HTTP/1.1
|
|
Authorization: Bearer <token>
|
|
Content-Type: application/json
|
|
|
|
```
|
|
|
|
| Role | Resultado |
|
|
|------|-----------|
|
|
| super_admin, ops_lead | 200 |
|
|
| technician (assigned ou unassigned) | 200 |
|
|
| technician (assigned to other) | 403 |
|
|
| noc | 403 |
|
|
|
|
---
|
|
|
|
## Webhook (inalterado — sem JWT)
|
|
|
|
```http
|
|
POST /api/v1/webhooks/onboard HTTP/1.1
|
|
Content-Type: application/json
|
|
X-Webhook-Secret: <WEBHOOK_SECRET>
|
|
|
|
```
|
|
|
|
JWT no lugar do secret → **401** (webhooks não aceitam Bearer).
|
|
|
|
---
|
|
|
|
## Health (público)
|
|
|
|
```http
|
|
GET /health HTTP/1.1
|
|
```
|
|
|
|
Sempre 200 sem auth (Traefik healthcheck).
|
|
|
|
---
|
|
|
|
## Role test matrix (curl)
|
|
|
|
```bash
|
|
API="http://10.10.10.122:8080"
|
|
TOKEN_ROOT=$(curl -sf -X POST "$API/api/v1/auth/login" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"username":"root","password":"805353"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
|
|
|
|
# Deve falhar sem token
|
|
curl -sf "$API/api/v1/desk/tickets" && echo UNEXPECTED || echo "401 OK"
|
|
|
|
# Deve funcionar com token
|
|
curl -sf -H "Authorization: Bearer $TOKEN_ROOT" "$API/api/v1/desk/tickets" | head -c 80
|
|
|
|
TOKEN_NOC=$(curl -sf -X POST "$API/api/v1/auth/login" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"username":"noc","password":"805353"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
|
|
|
|
# noc não pode fechar ticket
|
|
curl -sf -X PATCH -H "Authorization: Bearer $TOKEN_NOC" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"status":"closed"}' \
|
|
"$API/api/v1/desk/tickets/1" && echo UNEXPECTED || echo "403 OK"
|
|
```
|
|
|
|
---
|
|
|
|
## Error codes
|
|
|
|
| HTTP | Significado |
|
|
|------|-------------|
|
|
| 401 | Sem token, token inválido/expirado, credenciais login erradas |
|
|
| 403 | Token válido mas role insuficiente |
|
|
| 429 | Rate limit login |
|