Passa al contenuto principale

RBAC Matrix — Akira

Riferimento documento: Akira v5.2, cap. 8.2 (Authentication & Authorization) Versione: 1.1 — 2026-05-21 Owner: Security Engineering

1. Sintesi

Akira adotta un modello RBAC a due livelli con override puntuali:

  1. Ruolo principale sull'utente (user_accounts.role) → mappato a un set base di permission_key tramite la tabella role_permissions.
  2. Override puntuali sul singolo utente tramite user_extra_permissions che possono sia concedere (grant) che negare (deny) un permesso rispetto al ruolo.

La risoluzione è in cascade: deny esplicito vince sempre su grant esplicito che vince sul permesso di ruolo.

2. Schema DB di riferimento

-- Ruolo principale, vincolato a un set chiuso
ALTER TABLE user_accounts
ADD COLUMN role VARCHAR(32) NOT NULL DEFAULT 'sales'
CHECK (role IN ('super_admin','admin','operator','noc','finance','agent','sales',
'customer_admin','customer_user','customer_viewer'));

-- Matrice base ruolo → permission
CREATE TABLE role_permissions (
role VARCHAR(32) NOT NULL,
permission_key VARCHAR(96) NOT NULL,
PRIMARY KEY (role, permission_key)
);

-- Override puntuali (grant o deny) sul singolo utente
CREATE TABLE user_extra_permissions (
id BIGSERIAL PRIMARY KEY,
user_account_id BIGINT NOT NULL REFERENCES user_accounts(id) ON DELETE CASCADE,
permission_key VARCHAR(96) NOT NULL,
grant_or_deny VARCHAR(8) NOT NULL CHECK (grant_or_deny IN ('grant','deny')),
granted_by BIGINT REFERENCES user_accounts(id),
granted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expires_at TIMESTAMPTZ NULL,
reason TEXT,
UNIQUE (user_account_id, permission_key)
);

CREATE INDEX idx_user_extra_perms_user ON user_extra_permissions(user_account_id);

Funzione di risoluzione

CREATE OR REPLACE FUNCTION user_has_permission(p_user_id BIGINT, p_key VARCHAR)
RETURNS BOOLEAN LANGUAGE sql STABLE AS $$
WITH override AS (
SELECT grant_or_deny FROM user_extra_permissions
WHERE user_account_id = p_user_id
AND permission_key = p_key
AND (expires_at IS NULL OR expires_at > now())
)
SELECT CASE
WHEN (SELECT grant_or_deny FROM override) = 'deny' THEN FALSE
WHEN (SELECT grant_or_deny FROM override) = 'grant' THEN TRUE
ELSE EXISTS (
SELECT 1 FROM role_permissions rp
JOIN user_accounts ua ON ua.role = rp.role
WHERE ua.id = p_user_id AND rp.permission_key = p_key
)
END;
$$;

3. Permission keys (98)

Naming convention: <modulo>.<oggetto?>.<azione>. Azione sensibile esplicitata.

3.1 Audit

  • audit.read

3.2 Companies

  • companies.read
  • companies.write
  • companies.delete
  • companies.stop (sospensione operativa, non delete)
  • companies.balance.adjust (azione finanziaria, MFA re-challenge)

3.2 Accounts

  • accounts.read
  • accounts.write
  • accounts.delete (soft delete amministrativo, MFA re-challenge)

3.3 Originators

  • originators.read
  • originators.write
  • originators.delete
  • originators.activate

3.4 Terminators

  • terminators.read
  • terminators.write
  • terminators.delete
  • terminators.health_test

3.5 Devices

  • devices.read
  • devices.write
  • devices.delete
  • devices.sip_password.set (azione sensibile, MFA re-challenge consigliato)

3.6 Tariffs

  • tariffs.read
  • tariffs.write
  • tariffs.delete
  • tariffs.upload_csv
  • tariffs.send_mail
  • tariffs.rebill.run (rebill markup reseller — clona tariffe, MFA re-challenge obbligatorio)
  • tariffs.rebill.dry_run (rebill CDR storici: crea task + dry-run, nessuna scrittura sui CDR)
  • tariffs.rebill.apply (rebill CDR storici: applica il re-rating ai CDR, MFA re-challenge obbligatorio)
  • tariffs.rebill.rollback (rebill CDR storici: ripristina i CDR dall'audit trail, MFA re-challenge obbligatorio)

3.7 Destinations

  • destinations.read
  • destinations.write
  • destinations.delete
  • destinations.import

3.8 Routing

  • routing.read
  • routing.write
  • routing.failover_policy.edit
  • routing.access_policy.edit

3.9 Balance & Finance

  • balance.read
  • billing.read
  • billing.read.all
  • invoices.read
  • invoices.create
  • invoices.send
  • payments.read
  • payments.create
  • payments.reconcile
  • expenses.read
  • expenses.write

3.10 Reports

  • reports.cdr.read
  • reports.read
  • reports.admin
  • cdr.read
  • cdr.read_unmasked
  • cdr.recording.read
  • cdr.raw.read
  • cdr.export
  • reports.active_calls.read
  • live_calls.read
  • reports.summary.read
  • reports.export

3.11 Pattern Analyzer (fraud)

  • fraud.read
  • fraud.run_analysis
  • fraud.block_cli
  • fraud.block_destination
  • pattern.read
  • pattern.run

3.12 NOC TT (Quality Verifier + Tickets)

  • quality_alerts.read
  • quality_alerts.write
  • noc.read
  • noctt.read
  • noctt.run_test
  • noctt.recording.listen
  • noctt.ticket.send
  • benchmark.run

3.13 Settings

  • settings.read
  • settings.write
  • settings.system
  • settings.delete
  • settings.users.manage
  • settings.mailboxes.manage
  • settings.email.config
  • settings.bank.config
  • settings.telegram.config

3.14 Secrets Vault

  • secrets_vault.read
  • secrets_vault.rotate (azione sensibile, MFA re-challenge obbligatorio)
  • secrets_vault.delete (azione sensibile, MFA re-challenge obbligatorio)

3.15 Agents (cap. 30)

  • agents.read
  • agents.write
  • agents.fee_rules.edit
  • agents.payouts.run

3.16 AgentCore tools (cap. 48)

  • agentcore.tool.read_only (query_, report_, health_check_*)
  • agentcore.tool.write (create_, update_, activate_*)
  • agentcore.tool.destructive (delete_, block_, disable_*) — MFA re-challenge

3.17 Admin tools

  • admin.agents.manage
  • admin.ai_agent.manage
  • admin.users.manage
  • admin.bulk_import
  • admin.auto_upload.destinations

3.18 Wizard

  • wizard.use

3.19 System

  • system.audit.read
  • system.users.manage
  • system.maintenance

4. Matrice ruolo × permission

super_admin è un ruolo persistito separato da admin e, finché non esistono permission system-owner dedicate, eredita l'intero set di permission di admin.

Legenda: concesso; non concesso; MFA concesso ma richiede MFA re-challenge.

Permissionadminoperatornocfinanceagentsales
audit.read
companies.read✓*
companies.write
companies.deleteMFA
companies.stop
companies.balance.adjustMFAMFA
accounts.read
accounts.write
accounts.deleteMFA
originators.read
originators.write
originators.deleteMFA
originators.activate
terminators.read
terminators.write
terminators.deleteMFA
terminators.health_test
devices.read
devices.write
devices.deleteMFA
devices.sip_password.setMFAMFA
tariffs.read✓*
tariffs.write
tariffs.deleteMFA
tariffs.upload_csv
tariffs.send_mail
tariffs.rebill.runMFA
destinations.read
destinations.write
destinations.deleteMFA
destinations.import
routing.read
routing.write
routing.failover_policy.edit
routing.access_policy.edit
balance.read✓*
billing.read✓*
billing.read.all
invoices.read
invoices.create
invoices.send
payments.read
payments.create
payments.reconcile
expenses.read
expenses.write
reports.cdr.read✓*
reports.read
reports.admin
cdr.read
cdr.read_unmasked
cdr.recording.read
cdr.raw.read
cdr.export
reports.active_calls.read
live_calls.read
reports.summary.read✓*
reports.export
fraud.read
fraud.run_analysis
fraud.block_cli
fraud.block_destination
pattern.read
pattern.run
quality_alerts.read
quality_alerts.write
noc.read
noctt.read
noctt.run_test
noctt.recording.listen
noctt.ticket.send
benchmark.run
settings.read
settings.write
settings.system
settings.deleteMFA
settings.users.manageMFA
settings.mailboxes.manage
settings.email.config
settings.bank.configMFAMFA
settings.telegram.config
secrets_vault.read
secrets_vault.rotateMFA
secrets_vault.deleteMFA
agents.read✓*
agents.write
agents.fee_rules.edit
agents.payouts.runMFAMFA
agentcore.tool.read_only
agentcore.tool.write✓†
agentcore.tool.destructiveMFA
admin.agents.manage
admin.ai_agent.manage
admin.users.manage
admin.bulk_import
admin.auto_upload.destinations
wizard.use
system.audit.read
system.users.manageMFA
system.maintenanceMFA

Note:

  • ✓* per agent: filtro app-level che restringe ai soli company_id posseduti.
  • ✓† per noc su agentcore.tool.write: limitato a tool fraud-related (block_cli, block_destination); per tutto il resto vale read_only.

4.1 Customer tenant roles

I ruoli customer sono scaffold GA per il customer portal e richiedono user_accounts.company_id valorizzato. Lo scope tenant viene applicato dal backend tramite request.state.tenant_scope.

Permissioncustomer_admincustomer_usercustomer_viewer
companies.read
balance.read
billing.read
reports.cdr.read
reports.read
reports.admin
cdr.read
cdr.export
reports.summary.read
reports.export

5. Confirmation flow (MFA re-challenge)

Per le azioni marcate MFA la UI/API impone uno step aggiuntivo di MFA re-challenge (TOTP o recovery code) prima dell'esecuzione, anche se la sessione corrente è già autenticata. Lista esaustiva:

  • Qualsiasi *.delete
  • Qualsiasi agentcore.tool.destructive
  • companies.balance.adjust
  • tariffs.rebill.run
  • agents.payouts.run
  • settings.users.manage, system.users.manage, system.maintenance
  • settings.bank.config
  • devices.sip_password.set
  • secrets_vault.rotate, secrets_vault.delete

Backend: header X-MFA-Challenge-Token valido per 5 minuti dalla verifica TOTP. Senza token valido → 403 mfa_required.

6. Implementazione FastAPI

6.1 Decorator

# apps/backend/akira_backend/core/permissions.py
from functools import wraps
from fastapi import HTTPException, Request
from .auth import get_current_user
from .db import db_session

SENSITIVE_PERMISSIONS = {
"companies.delete", "originators.delete", "terminators.delete",
"devices.delete", "tariffs.delete", "destinations.delete",
"agentcore.tool.destructive",
"companies.balance.adjust", "tariffs.rebill.run",
"agents.payouts.run", "settings.users.manage",
"system.users.manage", "system.maintenance", "settings.delete",
"settings.bank.config", "devices.sip_password.set",
}

def require_permission(permission_key: str):
def decorator(func):
@wraps(func)
async def wrapper(*args, request: Request, user=None, **kwargs):
user = user or await get_current_user(request)
async with db_session() as s:
allowed = await s.scalar(
"SELECT user_has_permission(:uid, :key)",
{"uid": user.id, "key": permission_key},
)
if not allowed:
raise HTTPException(403, f"permission_denied:{permission_key}")
if permission_key in SENSITIVE_PERMISSIONS:
token = request.headers.get("X-MFA-Challenge-Token")
if not await verify_mfa_challenge_token(user.id, token):
raise HTTPException(403, "mfa_required")
return await func(*args, request=request, user=user, **kwargs)
return wrapper
return decorator

6.2 Esempio uso su route

from akira_backend.core.permissions import require_permission

@router.delete("/companies/{id}")
@require_permission("companies.delete")
async def delete_company(id: int, user: User = Depends(get_current_user)):
...

6.3 Seed iniziale role_permissions

INSERT INTO role_permissions(role, permission_key) VALUES
('super_admin','companies.read'),
('super_admin','companies.write'),
('super_admin','companies.delete'),
('admin','companies.read'),
('admin','companies.write'),
('admin','companies.delete'),
-- … (tutte le permission, generate da fixture python)
('sales','companies.read'),
('sales','companies.write'),
('sales','tariffs.read'),
('sales','reports.cdr.read');

Generazione fixture: apps/backend/scripts/seed_rbac.py parsa questo documento e produce le INSERT.

7. Audit

Ogni decisione di autorizzazione su permission MFA o *.delete viene loggata in system_audit_log(user_id, permission_key, decision, resource, request_id, ts). Retention minima: 2 anni.

8. Decisioni di mappa permessi

Le scelte che possono apparire arbitrarie nella matrice operator/noc sono documentate qui per fissare il razionale (riferimento: cap. 8.2 / 49.2).

8.1 Operator vede monitoring e report

  • Razionale: operator e il ruolo Operations interno Akira, responsabile di client onboarding, routing e supervisione del traffico. Visibilita su Active Calls (reports.active_calls.read, live_calls.read), Reports (reports.read, reports.admin, reports.export) e NOC TT (noctt.read, noc.read, quality_alerts.read, fraud.read, pattern.read) e` parte del set base necessario per il day-to-day operativo, non un privilegio elevato.
  • Operazioni sensibili sui ticket / quality alerts (noctt.ticket.send, noctt.run_test, noctt.recording.listen, quality_alerts.write, fraud.run_analysis, fraud.block_*, pattern.run) restano riservate al ruolo noc.

8.2 Tickets condivide noctt.read con NOC TT

  • I moduli /tickets e /noc-tt (frontend) e i router routers/tickets.py, routers/reports/quality.py (backend) richiedono tutti noctt.read. Non esiste un permesso tickets.read separato.
  • Razionale: come dichiarato nella sez. 3.12, il modulo NOC TT include sia il Quality Verifier che la gestione Tickets — sono due viste sulla stessa pipeline di incident management, quindi un unico gate di lettura coerente. Splittare i due permessi creerebbe stati incoerenti del tipo "vedo i ticket ma non l'alert che li ha generati".
  • Conseguenza: tutti i ruoli che hanno noctt.read (admin, super_admin, operator, noc) vedono entrambe le pagine; gli altri ruoli non le vedono. Questa e` la decisione presa in TASK-424 dopo i finding di re-audit AUD-058 / 068 / 077.