ligbox-ops-platform/projects/ops-desk/api/app/desk_governance_store.py
Ligbox Spec Hub b03bb2c37c feat(desk): UI/API Spec 039-041 + deploy atómico e smoke GREEN
Commita governance, user-wizard, operational-feed e catálogo RBAC;
adiciona deploy-desk-full.sh, smoke-desk.sh e regra anti-deploy parcial;
documenta credencial VM112 @betinplace.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 13:32:15 +00:00

263 lines
7.9 KiB
Python

"""Desk governance — audit log, user meta, module access. Spec 040 · DS-API-001."""
from __future__ import annotations
import json
import secrets
import sqlite3
from datetime import datetime, timezone
from typing import Any
GOVERNANCE_MODULES = (
"desk",
"openpanel",
"billing",
"api",
"security",
"ai_agents",
)
ACCESS_LEVELS = ("none", "read", "partial", "full")
ROLE_GROUPS: dict[str, str] = {
"super_admin": "Ops",
"ops_lead": "Ops",
"technician": "Ops",
"noc": "Ops",
"sales_admin": "Comercial",
"sales_support": "Comercial",
"finance": "Negócio",
"marketing": "Negócio",
"seo": "Negócio",
"developer": "Plataforma",
"devops": "Plataforma",
"security_analyst": "Plataforma",
"content_editor": "Plataforma",
"agentic_operator": "Plataforma",
"partner": "Externo",
}
def _now() -> str:
return datetime.now(timezone.utc).isoformat()
def init_governance_schema(conn: sqlite3.Connection) -> None:
conn.executescript(
"""
CREATE TABLE IF NOT EXISTS desk_governance_audit (
id INTEGER PRIMARY KEY AUTOINCREMENT,
actor_username TEXT NOT NULL,
actor_role TEXT,
action TEXT NOT NULL,
target_type TEXT NOT NULL,
target_id TEXT NOT NULL,
summary TEXT NOT NULL,
payload_json TEXT,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_desk_gov_audit_target
ON desk_governance_audit(target_type, target_id);
CREATE INDEX IF NOT EXISTS idx_desk_gov_audit_created
ON desk_governance_audit(created_at DESC);
CREATE TABLE IF NOT EXISTS desk_user_meta (
username TEXT PRIMARY KEY,
internal_id TEXT NOT NULL UNIQUE,
main_group TEXT,
secondary_groups_json TEXT NOT NULL DEFAULT '[]',
account_status TEXT NOT NULL DEFAULT 'active',
force_password_change INTEGER NOT NULL DEFAULT 0,
api_access INTEGER NOT NULL DEFAULT 0,
notifications_enabled INTEGER NOT NULL DEFAULT 1,
module_permissions_json TEXT NOT NULL DEFAULT '{}',
invite_token TEXT,
notes TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
"""
)
def _gen_internal_id() -> str:
return f"LB-{secrets.token_hex(4).upper()}"
def _gen_invite_token() -> str:
return secrets.token_urlsafe(24)
def group_for_role(role: str) -> str:
return ROLE_GROUPS.get(role, "")
def default_module_permissions(role: str) -> dict[str, str]:
base = {m: "none" for m in GOVERNANCE_MODULES}
if role == "super_admin":
return {m: "full" for m in GOVERNANCE_MODULES}
if role in ("ops_lead", "devops"):
base.update({"desk": "full", "openpanel": "partial", "security": "partial", "api": "read"})
elif role == "technician":
base.update({"desk": "partial", "openpanel": "read"})
elif role == "finance":
base.update({"billing": "full", "desk": "read"})
elif role == "agentic_operator":
base.update({"ai_agents": "full", "desk": "partial"})
return base
def log_audit(
conn: sqlite3.Connection,
*,
actor_username: str,
actor_role: str | None,
action: str,
target_type: str,
target_id: str,
summary: str,
payload: dict | None = None,
) -> dict:
now = _now()
conn.execute(
"""
INSERT INTO desk_governance_audit
(actor_username, actor_role, action, target_type, target_id, summary, payload_json, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
actor_username,
actor_role,
action,
target_type,
target_id,
summary,
json.dumps(payload or {}, ensure_ascii=False),
now,
),
)
return {
"actor_username": actor_username,
"actor_role": actor_role,
"action": action,
"target_type": target_type,
"target_id": target_id,
"summary": summary,
"created_at": now,
}
def list_audit(
conn: sqlite3.Connection,
*,
target_type: str | None = None,
target_id: str | None = None,
limit: int = 50,
) -> list[dict]:
q = "SELECT * FROM desk_governance_audit WHERE 1=1"
params: list[Any] = []
if target_type:
q += " AND target_type = ?"
params.append(target_type)
if target_id:
q += " AND target_id = ?"
params.append(target_id)
q += " ORDER BY id DESC LIMIT ?"
params.append(limit)
rows = conn.execute(q, params).fetchall()
out = []
for row in rows:
item = dict(row)
try:
item["payload"] = json.loads(item.pop("payload_json") or "{}")
except json.JSONDecodeError:
item["payload"] = {}
out.append(item)
return out
def get_user_meta(conn: sqlite3.Connection, username: str) -> dict | None:
row = conn.execute(
"SELECT * FROM desk_user_meta WHERE username = ?",
(username,),
).fetchone()
if not row:
return None
item = dict(row)
try:
item["secondary_groups"] = json.loads(item.pop("secondary_groups_json") or "[]")
except json.JSONDecodeError:
item["secondary_groups"] = []
try:
item["module_permissions"] = json.loads(item.pop("module_permissions_json") or "{}")
except json.JSONDecodeError:
item["module_permissions"] = {}
item["force_password_change"] = bool(item.get("force_password_change"))
item["api_access"] = bool(item.get("api_access"))
item["notifications_enabled"] = bool(item.get("notifications_enabled"))
return item
def ensure_user_meta(conn: sqlite3.Connection, username: str, role: str) -> dict:
existing = get_user_meta(conn, username)
if existing:
return existing
now = _now()
perms = default_module_permissions(role)
conn.execute(
"""
INSERT INTO desk_user_meta
(username, internal_id, main_group, secondary_groups_json, account_status,
force_password_change, api_access, notifications_enabled, module_permissions_json,
invite_token, notes, created_at, updated_at)
VALUES (?, ?, ?, '[]', 'active', 0, 0, 1, ?, NULL, NULL, ?, ?)
""",
(
username,
_gen_internal_id(),
group_for_role(role),
json.dumps(perms),
now,
now,
),
)
return get_user_meta(conn, username) or {}
def upsert_user_meta(conn: sqlite3.Connection, username: str, **fields: Any) -> dict:
row = get_user_meta(conn, username)
now = _now()
if not row:
raise ValueError("user meta missing")
secondary = fields.get("secondary_groups", row.get("secondary_groups", []))
perms = fields.get("module_permissions", row.get("module_permissions", {}))
conn.execute(
"""
UPDATE desk_user_meta SET
main_group = ?,
secondary_groups_json = ?,
account_status = ?,
force_password_change = ?,
api_access = ?,
notifications_enabled = ?,
module_permissions_json = ?,
invite_token = ?,
notes = ?,
updated_at = ?
WHERE username = ?
""",
(
fields.get("main_group", row.get("main_group")),
json.dumps(secondary),
fields.get("account_status", row.get("account_status", "active")),
1 if fields.get("force_password_change", row.get("force_password_change")) else 0,
1 if fields.get("api_access", row.get("api_access")) else 0,
1 if fields.get("notifications_enabled", row.get("notifications_enabled", True)) else 0,
json.dumps(perms),
fields.get("invite_token", row.get("invite_token")),
fields.get("notes", row.get("notes")),
now,
username,
),
)
return get_user_meta(conn, username) or {}