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.
495 lines
26 KiB
Python
495 lines
26 KiB
Python
"""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": "A0–A7 — 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",
|
||
},
|
||
}
|