ligbox-ops-platform/projects/ops-desk/api/app/rbac_matrix.py
Ligbox Spec Hub 6c4b063f76 Implement Spec 027 access matrix preview and agent bindings enforcement.
Adds RBAC matrix API/UI behind feature flags, agent×role toggles with audit,
and extends Agentic Ops with streaming chat, Kimi LLM support, and UI updates.
2026-06-20 22:02:03 +00:00

495 lines
26 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Exportação read-only da matriz Spec 027 para UI preview."""
from __future__ import annotations
import os
from typing import Any
from app.platform_role_catalog import catalog_export
from app.agents.catalog import AGENT_CATALOG
from app import agent_bindings
# Legenda global Spec 027
LEGEND = {
"full": {"symbol": "", "label": "Acesso total", "access": "full"},
"read": {"symbol": "🔒", "label": "Só leitura", "access": "read"},
"link": {"symbol": "🔗", "label": "Deep-link", "access": "link"},
"api": {"symbol": "⚙️", "label": "Via API Desk", "access": "api"},
"system": {"symbol": "🤖", "label": "Conta sistema", "access": "system"},
"none": {"symbol": "", "label": "Sem acesso", "access": "none"},
}
ROLE_COLUMNS = [
"super_admin", "ops_lead", "technician", "noc", "finance",
"sales_admin", "sales_support", "marketing", "seo", "developer",
"devops", "security_analyst", "content_editor", "agentic_operator",
]
# Spec 027 §3.1 — módulos Desk × função
DESK_MODULE_MATRIX: dict[str, dict[str, str]] = {
"core": {
"super_admin": "full", "ops_lead": "full", "technician": "full", "noc": "read",
"finance": "read", "sales_admin": "full", "sales_support": "full",
"marketing": "read", "seo": "read", "developer": "read", "devops": "read",
"security_analyst": "read", "content_editor": "read", "agentic_operator": "read",
},
"overview": {
"super_admin": "full", "ops_lead": "full", "technician": "read", "noc": "read",
"finance": "read", "sales_admin": "full", "sales_support": "read",
"marketing": "read", "seo": "read", "developer": "read", "devops": "read",
"security_analyst": "read", "content_editor": "none", "agentic_operator": "read",
},
"overview-home": {
"super_admin": "full", "ops_lead": "full", "technician": "full", "noc": "read",
"finance": "read", "sales_admin": "full", "sales_support": "full",
"marketing": "read", "seo": "read", "developer": "read", "devops": "full",
"security_analyst": "read", "content_editor": "read", "agentic_operator": "read",
},
"infra": {
"super_admin": "full", "ops_lead": "full", "technician": "read", "noc": "read",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "read", "developer": "read", "devops": "none",
"security_analyst": "full", "content_editor": "none", "agentic_operator": "read",
},
"infra2-soc": {
"super_admin": "full", "ops_lead": "full", "technician": "read", "noc": "read",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "none", "devops": "none",
"security_analyst": "full", "content_editor": "none", "agentic_operator": "read",
},
"funnel-timing": {
"super_admin": "full", "ops_lead": "full", "technician": "full", "noc": "read",
"finance": "read", "sales_admin": "full", "sales_support": "full",
"marketing": "full", "seo": "full", "developer": "read", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "read",
},
"wazuh-soc": {
"super_admin": "full", "ops_lead": "full", "technician": "read", "noc": "read",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "none", "devops": "read",
"security_analyst": "full", "content_editor": "none", "agentic_operator": "read",
},
"leads": {
"super_admin": "full", "ops_lead": "full", "technician": "full", "noc": "none",
"finance": "read", "sales_admin": "full", "sales_support": "full",
"marketing": "full", "seo": "full", "developer": "none", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none",
},
"events": {
"super_admin": "full", "ops_lead": "full", "technician": "read", "noc": "read",
"finance": "read", "sales_admin": "read", "sales_support": "read",
"marketing": "read", "seo": "read", "developer": "api", "devops": "api",
"security_analyst": "read", "content_editor": "none", "agentic_operator": "api",
},
"tenants": {
"super_admin": "full", "ops_lead": "full", "technician": "full", "noc": "read",
"finance": "read", "sales_admin": "full", "sales_support": "read",
"marketing": "read", "seo": "read", "developer": "read", "devops": "read",
"security_analyst": "read", "content_editor": "none", "agentic_operator": "read",
},
"messages": {
"super_admin": "full", "ops_lead": "none", "technician": "none", "noc": "none",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "none", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none",
},
"admin-users": {
"super_admin": "full", "ops_lead": "none", "technician": "none", "noc": "none",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "none", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none",
},
"billing-recurrence": {
"super_admin": "full", "ops_lead": "full", "technician": "read", "noc": "none",
"finance": "full", "sales_admin": "full", "sales_support": "read",
"marketing": "none", "seo": "none", "developer": "none", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none",
},
"migration": {
"super_admin": "full", "ops_lead": "full", "technician": "full", "noc": "read",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "none", "devops": "read",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none",
},
"assist": {
"super_admin": "full", "ops_lead": "full", "technician": "full", "noc": "read",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "none", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none",
},
"modules": {
"super_admin": "full", "ops_lead": "none", "technician": "none", "noc": "none",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "none", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none",
},
}
DESK_MODULE_LABELS: dict[str, str] = {
"core": "Núcleo (dashboard/tickets)",
"overview": "Audit Overview",
"overview-home": "Serviços IaaS",
"infra": "INFRA CODE",
"infra2-soc": "Infra 2 SOC",
"funnel-timing": "Relógio por fase",
"wazuh-soc": "Wazuh SOC",
"leads": "Leads / abandono CRM",
"events": "Eventos / webhooks",
"tenants": "Tenants",
"messages": "Mensagens (cadastro)",
"admin-users": "Administradores",
"billing-recurrence": "Billing recorrente",
"migration": "Migração e-mail",
"assist": "Assist / takeover",
"modules": "Toggle módulos",
}
AGENTS_LEGACY = [
{"id": "A0", "name": "Maestro", "role": "Orquestrador multi-agente", "approval": "agentic_operator / ops_lead"},
{"id": "A1", "name": "Node Health", "role": "CPU, RAM, serviços Carbonio", "approval": "ops_lead (restart)"},
{"id": "A2", "name": "Infra Mail", "role": "DNS, LE, Traefik, nginx", "approval": "devops / ops_lead"},
{"id": "A3", "name": "Deliverability", "role": "SPF/DKIM/DMARC", "approval": "seo / technician"},
{"id": "A4", "name": "Security Mail", "role": "amavis, spam, clamav", "approval": "security_analyst"},
{"id": "A5", "name": "Wazuh SOC", "role": "Correlação SIEM", "approval": "security_analyst / noc"},
{"id": "A6", "name": "Support Copilot", "role": "Assistência tickets", "approval": "technician"},
{"id": "A7", "name": "Remediation", "role": "Runbooks", "approval": "agentic_operator (obrigatório)"},
]
# Spec 027 §6 — governança Agentics (fonte: catalog.py + permissions + agents/routes.py)
AGENTIC_GOVERNANCE: dict[str, Any] = {
"use_ui": [
"super_admin", "ops_lead", "technician", "noc",
"agentic_operator", "developer", "devops", "security_analyst",
],
"trigger_runs": ["super_admin", "ops_lead", "agentic_operator"],
"approve_runbooks": ["super_admin", "ops_lead", "agentic_operator", "security_analyst"],
"configure_models": ["super_admin", "developer"],
"labels": {
"use_ui": "Usa módulo Agentics (chat, findings, inbox)",
"trigger_runs": "Dispara cenários manualmente",
"approve_runbooks": "Aprova runbooks A7 / remediação",
"configure_models": "Configura modelos/prompts LLM",
},
}
# Por agente: quem aprova acções sensíveis vs quem opera no dia-a-dia
AGENT_ROLE_MAP: dict[str, dict[str, list[str]]] = {
"A0": {
"approvers": ["agentic_operator", "ops_lead"],
"operators": ["super_admin", "ops_lead", "agentic_operator"],
},
"A1": {
"approvers": ["ops_lead"],
"operators": ["ops_lead", "devops", "noc", "super_admin", "agentic_operator"],
},
"A2": {
"approvers": ["devops", "ops_lead"],
"operators": ["devops", "ops_lead", "developer", "super_admin"],
},
"A3": {
"approvers": ["seo", "technician"],
"operators": ["seo", "technician", "ops_lead", "super_admin"],
},
"A4": {
"approvers": ["security_analyst"],
"operators": ["security_analyst", "ops_lead", "super_admin"],
},
"A5": {
"approvers": ["security_analyst", "noc"],
"operators": ["security_analyst", "noc", "ops_lead", "agentic_operator", "super_admin"],
},
"A6": {
"approvers": ["technician"],
"operators": ["technician", "ops_lead", "agentic_operator", "super_admin"],
},
"A7": {
"approvers": ["agentic_operator", "ops_lead", "super_admin"],
"operators": ["agentic_operator", "ops_lead", "super_admin"],
},
}
AGENT_RELATION_LABELS = {
"approve": "Aprova acções sensíveis",
"focus": "Operador principal",
"ui": "Acede UI Agentics",
}
CATEGORY_LABELS = {
"ops": "Operações",
"commercial": "Comercial",
"business": "Negócio",
"platform": "Plataforma",
"system": "Sistema",
}
# Spec 027 §2 / §4 / §5 — software em escopo (fora do Desk, dentro da plataforma Ligbox)
SOFTWARE_GROUPS: list[dict[str, Any]] = [
{
"id": "vm112",
"label": "VM112 — Onboard & Mail",
"host": "10.10.10.112",
"url": "https://onboard.ligbox.com.br",
"items": [
{"id": "wizard_assist", "label": "Wizard assist / takeover", "product": "ligbox-wizard",
"levels": {"super_admin": "full", "ops_lead": "full", "technician": "full", "noc": "read",
"finance": "read", "marketing": "read", "seo": "read", "developer": "api",
"sales_admin": "read", "sales_support": "read", "devops": "read",
"security_analyst": "read", "content_editor": "none", "agentic_operator": "read"}},
{"id": "vm112_api", "label": "API VM112 (:8090)", "product": "ligbox-wizard",
"levels": {"super_admin": "full", "ops_lead": "full", "technician": "api", "noc": "read",
"finance": "read", "marketing": "read", "seo": "read", "developer": "full",
"sales_admin": "read", "sales_support": "read", "devops": "full",
"security_analyst": "read", "content_editor": "none", "agentic_operator": "api"}},
{"id": "carbonio_admin", "label": "Carbonio admin (mail tenant)", "product": "Carbonio",
"levels": {"super_admin": "full", "ops_lead": "link", "technician": "link", "noc": "none",
"finance": "none", "marketing": "none", "seo": "none", "developer": "none",
"sales_admin": "none", "sales_support": "none", "devops": "link",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none"}},
{"id": "vm112_ssh", "label": "SSH VM112", "product": "Linux",
"levels": {"super_admin": "full", "ops_lead": "link", "technician": "none", "noc": "none",
"finance": "none", "marketing": "none", "seo": "none", "developer": "link",
"sales_admin": "none", "sales_support": "none", "devops": "full",
"security_analyst": "link", "content_editor": "none", "agentic_operator": "none"}},
{"id": "purge_domain", "label": "Purge domínio", "product": "ligbox-wizard",
"levels": {"super_admin": "full", "ops_lead": "full", "technician": "none", "noc": "none",
"finance": "none", "marketing": "none", "seo": "none", "developer": "none",
"sales_admin": "none", "sales_support": "none", "devops": "api",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none"}},
],
},
{
"id": "vm123",
"label": "VM123 — Finance & Hosting",
"host": "10.10.10.123",
"url": "https://financeiro.ligbox.com.br",
"items": [
{"id": "foss_admin", "label": "FOSSBilling Admin", "product": "FOSSBilling",
"levels": {"super_admin": "full", "ops_lead": "link", "technician": "none", "noc": "none",
"finance": "full", "sales_admin": "full", "sales_support": "full",
"marketing": "read", "seo": "none", "developer": "api", "devops": "none",
"security_analyst": "read", "content_editor": "none", "agentic_operator": "none"}},
{"id": "foss_client", "label": "FOSSBilling Cliente", "product": "FOSSBilling",
"levels": {"super_admin": "full", "ops_lead": "read", "technician": "none", "noc": "none",
"finance": "full", "sales_admin": "full", "sales_support": "full",
"marketing": "full", "seo": "none", "developer": "none", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none"}},
{"id": "odoo16", "label": "Odoo 16 (CRM / Invoicing)", "product": "Odoo",
"levels": {"super_admin": "full", "ops_lead": "link", "technician": "none", "noc": "none",
"finance": "full", "sales_admin": "full", "sales_support": "full",
"marketing": "none", "seo": "none", "developer": "api", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none"}},
{"id": "openpanel", "label": "OpenPanel (hosting sites)", "product": "OpenPanel",
"levels": {"super_admin": "full", "ops_lead": "link", "technician": "link", "noc": "none",
"finance": "read", "sales_admin": "link", "sales_support": "link",
"marketing": "full", "seo": "full", "developer": "api", "devops": "full",
"security_analyst": "read", "content_editor": "full", "agentic_operator": "none"}},
{"id": "openadmin", "label": "OpenAdmin", "product": "OpenPanel",
"levels": {"super_admin": "full", "ops_lead": "link", "technician": "none", "noc": "none",
"finance": "read", "sales_admin": "link", "sales_support": "none",
"marketing": "link", "seo": "link", "developer": "none", "devops": "full",
"security_analyst": "read", "content_editor": "none", "agentic_operator": "none"}},
{"id": "vm123_ssh", "label": "SSH VM123 (:2523)", "product": "Linux",
"levels": {"super_admin": "full", "ops_lead": "link", "technician": "none", "noc": "none",
"finance": "link", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "link", "devops": "full",
"security_analyst": "link", "content_editor": "none", "agentic_operator": "none"}},
],
},
{
"id": "externas",
"label": "Consolas & Infra (deep-link / API)",
"host": "",
"url": "",
"items": [
{"id": "cloudflare", "label": "Cloudflare DNS", "product": "Cloudflare",
"levels": {"super_admin": "full", "ops_lead": "full", "technician": "link", "noc": "none",
"finance": "none", "sales_admin": "read", "sales_support": "read",
"marketing": "read", "seo": "full", "developer": "none", "devops": "full",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none"}},
{"id": "traefik", "label": "Traefik CT114", "product": "Traefik",
"levels": {"super_admin": "full", "ops_lead": "link", "technician": "none", "noc": "none",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "link", "devops": "full",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none"}},
{"id": "proxmox", "label": "Proxmox host", "product": "Proxmox",
"levels": {"super_admin": "full", "ops_lead": "link", "technician": "none", "noc": "none",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "none", "devops": "full",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none"}},
{"id": "pfsense", "label": "pfSense API", "product": "pfSense",
"levels": {"super_admin": "full", "ops_lead": "link", "technician": "none", "noc": "none",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "none", "devops": "full",
"security_analyst": "link", "content_editor": "none", "agentic_operator": "none"}},
{"id": "wazuh", "label": "Wazuh VM104", "product": "Wazuh",
"levels": {"super_admin": "full", "ops_lead": "full", "technician": "read", "noc": "read",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "none", "devops": "read",
"security_analyst": "full", "content_editor": "none", "agentic_operator": "none"}},
{"id": "github", "label": "GitHub itecnologys/*", "product": "GitHub",
"levels": {"super_admin": "full", "ops_lead": "read", "technician": "none", "noc": "none",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "none", "seo": "none", "developer": "full", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none"}},
{"id": "gsc", "label": "Google Search Console", "product": "Google",
"levels": {"super_admin": "none", "ops_lead": "none", "technician": "none", "noc": "none",
"finance": "none", "sales_admin": "none", "sales_support": "none",
"marketing": "link", "seo": "full", "developer": "none", "devops": "none",
"security_analyst": "none", "content_editor": "none", "agentic_operator": "none"}},
],
},
]
def _software_groups_export() -> list[dict[str, Any]]:
out = []
for grp in SOFTWARE_GROUPS:
items = []
for item in grp["items"]:
levels = item.get("levels", {})
items.append({
**item,
"levels": {role: levels.get(role, "none") for role in ROLE_COLUMNS},
})
out.append({**grp, "items": items})
return out
def _agent_role_relations_from_db(conn, agent_id: str, role_id: str) -> list[str]:
return agent_bindings.agent_relations(conn, agent_id, role_id)
def _agents_export(conn) -> list[dict[str, Any]]:
order = ["A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7"]
matrix = agent_bindings.load_bindings_matrix(conn)
out: list[dict[str, Any]] = []
for aid in order:
p = AGENT_CATALOG.get(aid)
if not p:
continue
mapping = AGENT_ROLE_MAP.get(aid, {})
role_levels = {role: _agent_role_relations_from_db(conn, aid, role) for role in ROLE_COLUMNS}
role_bindings = matrix["agents"].get(aid, {})
out.append({
"id": p.id,
"codename": p.codename,
"name": p.name,
"role": p.role,
"reads": list(p.reads),
"actions": list(p.actions),
"approval_text": p.approval,
"approvers": mapping.get("approvers", []),
"operators": mapping.get("operators", []),
"scenarios": list(p.scenarios),
"role_relations": role_levels,
"bindings": role_bindings,
})
return out
def _role_agents_summary(conn, role_id: str) -> list[dict[str, Any]]:
rows = []
for agent in _agents_export(conn):
rels = agent["role_relations"].get(role_id, [])
if not rels:
continue
rows.append({
"id": agent["id"],
"name": agent["name"],
"role": agent["role"],
"relations": rels,
"relation_labels": [AGENT_RELATION_LABELS[r] for r in rels if r in AGENT_RELATION_LABELS],
"is_approver": "approve" in rels,
"is_operator": "focus" in rels,
})
return rows
def _role_agentic_caps(conn, role_id: str) -> list[dict[str, str]]:
return agent_bindings.role_agentic_caps(conn, role_id)
def _role_software_summary(role_id: str) -> list[dict[str, Any]]:
"""Visão «cartão de função» — todos os SW onde a função tem acesso ≠ none."""
rows = []
for grp in SOFTWARE_GROUPS:
for item in grp["items"]:
lv = item.get("levels", {}).get(role_id, "none")
if lv == "none":
continue
rows.append({
"group": grp["label"],
"product": item.get("product", ""),
"label": item["label"],
"level": lv,
"host": grp.get("host", ""),
"url": grp.get("url", ""),
})
return rows
def _all_role_software_summaries() -> dict[str, list[dict[str, Any]]]:
return {role: _role_software_summary(role) for role in ROLE_COLUMNS}
def _desk_modules_export() -> list[dict[str, Any]]:
rows = []
for mod_id, levels in DESK_MODULE_MATRIX.items():
rows.append({
"id": mod_id,
"label": DESK_MODULE_LABELS.get(mod_id, mod_id),
"levels": {role: levels.get(role, "none") for role in ROLE_COLUMNS},
})
return rows
def matrix_export(conn) -> dict[str, Any]:
catalog = catalog_export()
bindings_matrix = agent_bindings.load_bindings_matrix(conn)
edit_enabled = os.getenv("ACCESS_MATRIX_EDIT", "0").strip().lower() in ("1", "true", "yes", "on")
return {
"spec": "027",
"preview_mode": not edit_enabled,
"read_only": not edit_enabled,
"editable": edit_enabled,
"legend": LEGEND,
"role_columns": ROLE_COLUMNS,
"category_labels": CATEGORY_LABELS,
"desk_modules": _desk_modules_export(),
"software_groups": _software_groups_export(),
"role_software_summaries": _all_role_software_summaries(),
"role_agents_summaries": {role: _role_agents_summary(conn, role) for role in ROLE_COLUMNS},
"role_agentic_caps": {role: _role_agentic_caps(conn, role) for role in ROLE_COLUMNS},
"agent_bindings_matrix": bindings_matrix,
"agentic_governance": AGENTIC_GOVERNANCE,
"agent_relation_labels": AGENT_RELATION_LABELS,
"agents": _agents_export(conn),
"catalog": catalog,
"tabs": [
{"id": "overview", "label": "Visão da função", "type": "overview",
"hint": "Desk + software + APIs da função seleccionada"},
{"id": "vm122", "label": "Matriz Desk", "type": "desk_modules",
"hint": "VM122 — módulos internos da plataforma"},
{"id": "software", "label": "Software & Infra", "type": "software",
"hint": "VM112 · VM123 · consolas externas"},
{"id": "bindings", "label": "APIs & Grupos", "type": "catalog",
"hint": "Bindings Odoo-style — grupos, roles, permissões"},
{"id": "agents", "label": "Agentes IA", "type": "agents",
"hint": "A0A7 — orquestração e aprovações"},
],
"scope_layers": [
{"id": "vm122", "label": "Desk VM122", "host": "10.10.10.122", "desc": "Módulos internos"},
{"id": "vm112", "label": "Onboard VM112", "host": "10.10.10.112", "desc": "Wizard, mail, API"},
{"id": "vm123", "label": "Finance VM123", "host": "10.10.10.123", "desc": "FOSS, Odoo, OpenPanel"},
{"id": "externas", "label": "Infra externa", "host": "", "desc": "Cloudflare, Traefik, Proxmox…"},
],
"service_labels": {
"desk": "Desk VM122",
"vm112": "VM112 Wizard",
"vm123_foss": "FOSSBilling",
"vm123_odoo": "Odoo 16",
"vm123_openpanel": "OpenPanel",
"infra": "Infra / SSH",
"vm104": "Wazuh VM104",
},
}