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

3.6 KiB

Data Model: Desk Auth & RBAC (003)


desk_users (nova tabela)

Coluna Tipo Obrigatório Descrição
id INTEGER PK sim auto
username TEXT UNIQUE sim root, admin, mini, noc
password_hash TEXT sim bcrypt
role TEXT sim super_admin | ops_lead | technician | noc
display_name TEXT não ex. "Roger" para root
active INTEGER sim 1=activo, 0=desactivado
last_login_at TEXT ISO8601 não UTC
created_at TEXT ISO8601 sim UTC
updated_at TEXT ISO8601 sim UTC

Seed inicial

username role display_name active
root super_admin Roger 1
admin ops_lead Chefe Ops 1
mini technician Suporte 1
noc noc NOC 1

tickets (alteração)

Coluna nova Tipo Descrição
assigned_to TEXT NULL username do técnico responsável
assigned_at TEXT ISO8601 NULL quando foi atribuído

Migration SQL:

ALTER TABLE tickets ADD COLUMN assigned_to TEXT;
ALTER TABLE tickets ADD COLUMN assigned_at TEXT;

JWT payload

Claim Tipo Descrição
sub string username
role string role actual
exp int unix expiry
iat int issued at

Exemplo decodificado:

{
  "sub": "admin",
  "role": "ops_lead",
  "exp": 1749570000,
  "iat": 1749541200
}

Login request / response

POST /api/v1/auth/login

Request:

{
  "username": "admin",
  "password": "805353"
}

Response 200:

{
  "access_token": "eyJ...",
  "token_type": "bearer",
  "expires_in": 28800,
  "username": "admin",
  "role": "ops_lead",
  "display_name": "Chefe Ops"
}

Response 401:

{
  "detail": "invalid credentials"
}

Role enum

super_admin > ops_lead > technician > noc

Ordem hierárquica usada apenas para UI; permissões são explícitas na matriz (não herança automática).


Permission helpers (lógica)

def can_read_tickets(role: str) -> bool:
    return role in ALL_ROLES

def can_patch_ticket(role: str, ticket: dict, username: str) -> bool:
    if role in ("super_admin", "ops_lead"):
        return True
    if role == "technician":
        assignee = ticket.get("assigned_to")
        return assignee is None or assignee == username
    return False  # noc

def can_run_audit(role: str) -> bool:
    return role in ("super_admin", "ops_lead")

def can_manage_users(role: str) -> bool:
    return role == "super_admin"

def should_mask_ticket(role: str) -> bool:
    return role == "noc"

Masked ticket (noc view)

Campos removidos ou substituídos em company_profile:

Campo original Valor noc
tax_id ***
address {}
email_billing ***
email_legal ***
phone_landline ***
billing_state omitido
payload.funnel_notes[].data.company_profile mascarado recursivo

State: login session (client)

sessionStorage:
  ligbox_ops_token: "<jwt>"
  ligbox_ops_user: {"username","role","display_name","expires_at"}

Logout: clear sessionStorage → redirect /login.html


Endpoints auth (novos)

Method Path Auth Roles
POST /api/v1/auth/login público
POST /api/v1/auth/logout JWT all
GET /api/v1/auth/me JWT all
GET /api/v1/auth/users JWT super_admin
PATCH /api/v1/auth/users/{username} JWT super_admin