diff --git a/projects/ops-desk/api/app/permissions.py b/projects/ops-desk/api/app/permissions.py index 62d68d1..ef93de4 100644 --- a/projects/ops-desk/api/app/permissions.py +++ b/projects/ops-desk/api/app/permissions.py @@ -1,19 +1,71 @@ -"""RBAC helpers for Ligbox Ops Desk.""" +"""RBAC helpers for Ligbox Ops Desk — Spec 003 + 027.""" from __future__ import annotations -ROLES = frozenset({"super_admin", "ops_lead", "technician", "noc"}) +# Ops (Spec 003) +OPS_ROLES = frozenset({"super_admin", "ops_lead", "technician", "noc"}) -ROLE_LABELS = { +# Comercial (Spec 027) +SALES_ROLES = frozenset({"sales_admin", "sales_support"}) + +# Negócio / plataforma (Spec 027) +BUSINESS_ROLES = frozenset( + { + "finance", + "marketing", + "seo", + "developer", + "devops", + "security_analyst", + "content_editor", + "agentic_operator", + } +) + +# Sistema (não humanos) +SYSTEM_ROLES = frozenset({"api_service", "agent_system"}) + +ALL_ROLES = OPS_ROLES | SALES_ROLES | BUSINESS_ROLES | SYSTEM_ROLES + +# Funções humanas (login Desk) +HUMAN_ROLES = OPS_ROLES | SALES_ROLES | BUSINESS_ROLES + +# Atribuíveis no cadastro Spec 004 (exceto super_admin) +ASSIGNABLE_ROLES = HUMAN_ROLES - {"super_admin"} + +# Compatibilidade com código existente +ROLES = HUMAN_ROLES + +ROLE_LABELS: dict[str, str] = { "super_admin": "Super Admin", "ops_lead": "Chefe Ops", "technician": "Suporte", "noc": "NOC", + "sales_admin": "Sales Admin", + "sales_support": "Sales Support", + "finance": "Financeiro", + "marketing": "Marketing", + "seo": "SEO", + "developer": "Developer", + "devops": "DevOps", + "security_analyst": "Segurança / SOC", + "content_editor": "Conteúdo / CMS", + "agentic_operator": "Operador Agentes IA", + "api_service": "API Service", + "agent_system": "Agent System", } +def is_valid_role(role: str) -> bool: + return role in ALL_ROLES + + +def is_assignable_role(role: str) -> bool: + return role in ASSIGNABLE_ROLES + + def can_read_tickets(role: str) -> bool: - return role in ROLES + return role in HUMAN_ROLES def can_patch_ticket(role: str, ticket: dict, username: str) -> bool: @@ -38,37 +90,99 @@ def can_run_audit(role: str) -> bool: def can_read_audit_overview(role: str) -> bool: - return role in ("super_admin", "ops_lead", "noc") + return role in ( + "super_admin", + "ops_lead", + "noc", + "developer", + "devops", + "security_analyst", + "agentic_operator", + ) def can_read_audit_scorecard(role: str) -> bool: - return role in ("super_admin", "ops_lead", "noc") + return role in ( + "super_admin", + "ops_lead", + "noc", + "developer", + "security_analyst", + "agentic_operator", + ) def can_read_cloudflare_dns(role: str) -> bool: - return role in ("super_admin", "ops_lead", "technician", "noc") + return role in ( + "super_admin", + "ops_lead", + "technician", + "noc", + "seo", + "devops", + "developer", + ) def can_read_funnel(role: str) -> bool: - return role in ROLES + return role in ( + "super_admin", + "ops_lead", + "technician", + "noc", + "sales_admin", + "sales_support", + "finance", + "marketing", + "seo", + "developer", + "devops", + "agentic_operator", + ) def can_read_session_timeline(role: str) -> bool: - return role in ("super_admin", "ops_lead", "technician") + return role in ( + "super_admin", + "ops_lead", + "technician", + "sales_admin", + "sales_support", + "finance", + "marketing", + "seo", + "developer", + "devops", + "agentic_operator", + ) def can_list_webhook_events(role: str, source: str | None = None) -> bool: if role == "noc": return source in (None, "wazuh", "vm112-security") - return role in ROLES + if role == "security_analyst": + return source in (None, "wazuh", "vm112-security", "vm112") + if role == "finance": + return source in (None, "billing", "vm112") + if role == "developer": + return source in (None, "vm112", "wazuh") + return role in HUMAN_ROLES def can_read_crm_leads(role: str) -> bool: - return role in ("super_admin", "ops_lead", "technician") + return role in ( + "super_admin", + "ops_lead", + "technician", + "sales_admin", + "sales_support", + "marketing", + "seo", + ) def can_read_assist(role: str) -> bool: - return role in ROLES + return role in ("super_admin", "ops_lead", "technician", "sales_admin", "sales_support") def can_assist_takeover(role: str) -> bool: @@ -85,15 +199,15 @@ def can_manage_users(role: str) -> bool: def can_manage_vm112_domains(role: str) -> bool: """Admin Desk — domínios orquestrados VM112 (Spec 017).""" - return role in ("super_admin", "ops_lead") + return role in ("super_admin", "ops_lead", "devops") def should_mask_sensitive(role: str) -> bool: - return role == "noc" + return role in ("noc", "sales_support") def can_read_migration(role: str) -> bool: - return role in ("super_admin", "ops_lead", "technician", "noc") + return role in ("super_admin", "ops_lead", "technician", "noc", "devops") def can_manage_migration(role: str) -> bool: @@ -101,8 +215,67 @@ def can_manage_migration(role: str) -> bool: def can_read_billing(role: str) -> bool: - return role in ROLES + return role in ( + "super_admin", + "ops_lead", + "noc", + "finance", + "sales_admin", + "sales_support", + ) + + +def can_validate_billing(role: str) -> bool: + """Transicionar billing_state — Spec 023 / FR-027-005 / FR-027-009.""" + return role in ("super_admin", "ops_lead", "finance", "sales_admin") def can_manage_billing(role: str) -> bool: - return role in ("super_admin", "ops_lead") + return can_validate_billing(role) + + +def can_create_foss_order(role: str) -> bool: + return role in ( + "super_admin", + "ops_lead", + "finance", + "sales_admin", + "sales_support", + ) + + +def can_access_foss_admin(role: str) -> bool: + return role in ("super_admin", "finance", "sales_admin") + + +def can_access_openadmin(role: str) -> bool: + return role in ("super_admin", "devops", "sales_admin") + + +def can_openpanel_autologin(role: str) -> bool: + return role in ( + "super_admin", + "sales_admin", + "sales_support", + "marketing", + "seo", + "content_editor", + "technician", + ) + + +def can_openpanel_provision(role: str) -> bool: + return role in ("super_admin", "devops", "sales_admin", "sales_support") + + +def can_openpanel_delete(role: str) -> bool: + return role in ("super_admin", "devops") + + +def roles_meta() -> dict: + """Metadados para UI — labels e funções atribuíveis no cadastro.""" + return { + "labels": ROLE_LABELS, + "assignable": sorted(ASSIGNABLE_ROLES), + "human": sorted(HUMAN_ROLES), + }