"""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", }, }