ligbox-ops-platform/app/platform_role_catalog.py
Ligbox Spec Hub 3a2c64834b Initial import: ligbox-ops-platform + specs + LAPTOP + obsidian merge (CT130)
Source: VM122 /opt + obsidian-infra + LAPTOP
Hub: CT130 spec-hub 10.10.10.130
2026-06-19 17:26:41 +00:00

277 lines
10 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.

"""Catálogo mestre de funções Ligbox — padrão Odoo res.groups aplicado à plataforma DevOps.
Uma função Desk (`desk_role`) é a **fonte de verdade**; cada serviço/VM recebe um
mapeamento explícito (grupo nativo, módulo, permissão API). Spec 027.
Analogia Odoo:
res.users.role → desk_users.role (VM122)
res.groups → PLATFORM_ROLE_CATALOG[*].bindings[*]
ir.model.access → permissions.py helpers + route guards
record rules → should_mask_sensitive, ticket assignee, …
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
from app.modules.registry import ROLE_MODULE_DEFAULTS, role_module_defaults
@dataclass(frozen=True)
class ServiceBinding:
"""Como uma função se materializa num serviço concreto."""
service: str # desk | vm112 | vm123_foss | vm123_odoo | vm123_openpanel | infra
binding_type: str # group | module | permission | deep_link | ssh
value: str
access: str = "full" # full | read | link | api | none
@dataclass(frozen=True)
class PlatformRole:
id: str
label: str
category: str # ops | commercial | business | platform | system
description: str
bindings: tuple[ServiceBinding, ...] = ()
desk_modules: tuple[str, ...] | None = None # None = legacy ops (global toggles)
def _desk_perms(*perms: str) -> tuple[ServiceBinding, ...]:
return tuple(ServiceBinding("desk", "permission", p) for p in perms)
# ── Catálogo mestre (fonte única para docs, provisionamento e introspecção) ──
PLATFORM_ROLE_CATALOG: dict[str, PlatformRole] = {
"super_admin": PlatformRole(
id="super_admin",
label="Super Admin",
category="ops",
description="Dono — users, tenants, purge, config global",
desk_modules=None,
bindings=(
ServiceBinding("vm123_odoo", "group", "base.group_system"),
ServiceBinding("vm123_foss", "group", "admin"),
ServiceBinding("vm123_openpanel", "role", "Super Admin"),
ServiceBinding("vm112", "permission", "assist.takeover"),
ServiceBinding("vm112", "permission", "purge.domain"),
ServiceBinding("infra", "access", "ssh", "full"),
*_desk_perms("manage_users", "manage_billing", "run_audit", "manage_vm112_domains"),
),
),
"ops_lead": PlatformRole(
id="ops_lead",
label="Chefe Ops",
category="ops",
description="Gestão operacional, audit, tickets, domínios VM112",
desk_modules=None,
bindings=(
ServiceBinding("vm112", "permission", "assist.takeover"),
ServiceBinding("vm112", "permission", "purge.domain"),
ServiceBinding("infra", "access", "ssh", "link"),
*_desk_perms("run_audit", "manage_vm112_domains", "manage_billing"),
),
),
"technician": PlatformRole(
id="technician",
label="Suporte",
category="ops",
description="Tickets atribuídos, assist wizard, migração",
desk_modules=None,
bindings=(
ServiceBinding("vm112", "permission", "assist.takeover"),
ServiceBinding("vm123_openpanel", "permission", "autologin"),
*_desk_perms("patch_assigned_tickets", "read_migration"),
),
),
"noc": PlatformRole(
id="noc",
label="NOC",
category="ops",
description="Monitorização read-only — dados sensíveis mascarados",
desk_modules=None,
bindings=(
ServiceBinding("desk", "permission", "mask_sensitive"),
ServiceBinding("vm104", "permission", "wazuh.read"),
*_desk_perms("read_tickets", "read_billing"),
),
),
"sales_admin": PlatformRole(
id="sales_admin",
label="Sales Admin",
category="commercial",
description="Gerente comercial — pipeline, billing validation, FOSS+Odoo manager",
desk_modules=tuple(ROLE_MODULE_DEFAULTS["sales_admin"]),
bindings=(
ServiceBinding("vm123_foss", "group", "ligbox-sales-admin"),
ServiceBinding("vm123_odoo", "group", "sales_team.group_sale_manager"),
ServiceBinding("vm123_openpanel", "role", "Admin"),
*_desk_perms("validate_billing", "create_foss_order", "read_crm_leads"),
),
),
"sales_support": PlatformRole(
id="sales_support",
label="Sales Support",
category="commercial",
description="Analista comercial — pedidos e CRM, sem validar billing",
desk_modules=tuple(ROLE_MODULE_DEFAULTS["sales_support"]),
bindings=(
ServiceBinding("vm123_foss", "group", "ligbox-sales-support"),
ServiceBinding("vm123_odoo", "group", "sales_team.group_sale_salesman"),
ServiceBinding("vm123_openpanel", "permission", "autologin"),
*_desk_perms("create_foss_order", "read_crm_leads"),
),
),
"finance": PlatformRole(
id="finance",
label="Financeiro",
category="business",
description="FOSSBilling, Odoo fiscal, validação billing",
desk_modules=tuple(ROLE_MODULE_DEFAULTS["finance"]),
bindings=(
ServiceBinding("vm123_foss", "group", "ligbox-finance-admin"),
ServiceBinding("vm123_odoo", "group", "account.group_account_manager"),
ServiceBinding("vm123_odoo", "group", "account.group_account_invoice"),
*_desk_perms("validate_billing", "create_foss_order"),
),
),
"marketing": PlatformRole(
id="marketing",
label="Marketing",
category="business",
description="Campanhas, leads, produtos FOSS",
desk_modules=tuple(ROLE_MODULE_DEFAULTS["marketing"]),
bindings=(
ServiceBinding("vm123_foss", "group", "ligbox-marketing"),
ServiceBinding("vm123_openpanel", "permission", "autologin"),
*_desk_perms("read_crm_leads", "read_funnel"),
),
),
"seo": PlatformRole(
id="seo",
label="SEO",
category="business",
description="DNS, Search Console, sites OpenPanel",
desk_modules=tuple(ROLE_MODULE_DEFAULTS["seo"]),
bindings=(
ServiceBinding("vm123_openpanel", "permission", "autologin"),
ServiceBinding("infra", "permission", "cloudflare_dns.read"),
*_desk_perms("read_funnel", "read_crm_leads"),
),
),
"developer": PlatformRole(
id="developer",
label="Developer",
category="platform",
description="Código wizard/Desk, GitHub, APIs",
desk_modules=tuple(ROLE_MODULE_DEFAULTS["developer"]),
bindings=(
ServiceBinding("vm123_foss", "group", "ligbox-dev-api"),
ServiceBinding("vm112", "permission", "api.dev_key"),
ServiceBinding("infra", "access", "github", "full"),
*_desk_perms("read_events"),
),
),
"devops": PlatformRole(
id="devops",
label="DevOps",
category="platform",
description="Proxmox, Traefik, pfSense, OpenPanel admin",
desk_modules=tuple(ROLE_MODULE_DEFAULTS["devops"]),
bindings=(
ServiceBinding("vm123_openpanel", "role", "Super Admin"),
ServiceBinding("infra", "access", "ssh", "full"),
ServiceBinding("infra", "permission", "proxmox", "full"),
*_desk_perms("manage_vm112_domains"),
),
),
"security_analyst": PlatformRole(
id="security_analyst",
label="Segurança / SOC",
category="platform",
description="Wazuh, incidentes, resposta",
desk_modules=tuple(ROLE_MODULE_DEFAULTS["security_analyst"]),
bindings=(
ServiceBinding("vm104", "permission", "wazuh.manage"),
*_desk_perms("read_audit_overview"),
),
),
"content_editor": PlatformRole(
id="content_editor",
label="Conteúdo / CMS",
category="platform",
description="Sites clientes OpenPanel",
desk_modules=tuple(ROLE_MODULE_DEFAULTS["content_editor"]),
bindings=(
ServiceBinding("vm123_openpanel", "permission", "autologin"),
),
),
"agentic_operator": PlatformRole(
id="agentic_operator",
label="Operador Agentes IA",
category="platform",
description="Aprova runbooks A7 e acções agentes",
desk_modules=tuple(ROLE_MODULE_DEFAULTS["agentic_operator"]),
bindings=(
ServiceBinding("desk", "permission", "approve_agent_remediation"),
),
),
"api_service": PlatformRole(
id="api_service",
label="API Service",
category="system",
description="M2M webhooks e workers",
desk_modules=("core",),
bindings=(
ServiceBinding("vm123_foss", "group", "ligbox-dev-api"),
ServiceBinding("vm123_odoo", "group", "base.group_system"),
),
),
"agent_system": PlatformRole(
id="agent_system",
label="Agent System",
category="system",
description="Conta dos agentes A0A7",
desk_modules=("core", "events"),
bindings=(),
),
}
def catalog_for_role(role_id: str) -> PlatformRole | None:
return PLATFORM_ROLE_CATALOG.get(role_id)
def bindings_for_service(role_id: str, service: str) -> list[ServiceBinding]:
role = catalog_for_role(role_id)
if not role:
return []
return [b for b in role.bindings if b.service == service]
def catalog_export() -> dict[str, Any]:
"""JSON para API / docs — visão unificada estilo Odoo groups."""
out: dict[str, Any] = {"roles": {}, "services": ["desk", "vm112", "vm123_foss", "vm123_odoo", "vm123_openpanel", "infra", "vm104"]}
for rid, role in PLATFORM_ROLE_CATALOG.items():
mods = role.desk_modules
if mods is None:
mods_list = list(role_module_defaults(rid) or []) # type: ignore[arg-type]
legacy = True
else:
mods_list = list(mods)
legacy = rid in ("super_admin", "ops_lead", "technician", "noc")
out["roles"][rid] = {
"id": rid,
"label": role.label,
"category": role.category,
"description": role.description,
"desk_modules": mods_list,
"desk_modules_legacy_global": legacy,
"bindings": [
{"service": b.service, "type": b.binding_type, "value": b.value, "access": b.access}
for b in role.bindings
],
}
return out