La trasparenza genera fiducia, ma una dashboard mal isolata diventa un incidente di protezione dei dati. Condividere gli indicatori di assistenza con il cliente riduce i solleciti via e-mail e comunica professionalità. La sfida tecnica non è disegnare il grafico: è garantire che ogni cliente veda solo i propri ticket. Ecco come farlo con GLPI + Metabase o Grafana, con il filtro per entità bloccato nel punto giusto.
Prima di pubblicare: tre decisioni che definiscono tutto
Nella gestione di ambienti GLPI multicliente, tre scelte decidono se la dashboard sarà un valore aggiunto o un problema di sicurezza:
- Dove bloccare il filtro per entità: nel link, nella query o nell'embed firmato. Solo l'ultimo resiste alla manomissione dell'URL.
- Quale data usare per il periodo: creazione (
date), risoluzione (solvedate) o chiusura (closedate). Mescolare le tre distorce ogni indicatore. - Da dove leggere i dati: il database primario o una replica in sola lettura. Metabase e Grafana lanciano query pesanti e competono con GLPI in produzione.
Passo 1 - un utente di database in sola lettura
Non puntare mai lo strumento di BI all'utente di GLPI. Crea un utente dedicato con SELECT e nient'altro. In MariaDB:
-- Utente in sola lettura per la BI, limitato alla rete interna
CREATE USER 'bi_ro'@'10.20.%' IDENTIFIED BY '...password_forte...';
GRANT SELECT ON glpi.* TO 'bi_ro'@'10.20.%';
-- Il limite di tempo per query evita che una dashboard blocchi il database (MariaDB)
SET GLOBAL max_statement_time = 15; -- secondi
FLUSH PRIVILEGES;
Se possibile, punta la BI a una replica di lettura invece che al database primario. Così un filtro dimenticato in un grafico non trattiene le transazioni di GLPI in produzione; abbiamo visto un singolo pannello con un JOIN scritto male degradare il tempo di risposta dell'intero help desk.
Passo 2 - isolare per entità senza esporre le sotto-entità
Ogni cliente è un'entità in GLPI, e l'istinto è filtrare per entities_id = X. Qui si nasconde la trappola più comune: se il cliente ha filiali o reparti come sotto-entità, quel filtro cattura solo l'entità radice e i numeri risultano più bassi della realtà. Bisogna includere tutto il sottoalbero. In MariaDB 10.2.2+ si risolve con una CTE ricorsiva:
-- Tutte le entità del cliente (radice + discendenti)
WITH RECURSIVE albero_cliente AS (
SELECT id FROM glpi_entities WHERE id = 4 -- entità radice del cliente
UNION ALL
SELECT e.id
FROM glpi_entities e
JOIN albero_cliente a ON e.entities_id = a.id
)
SELECT
COUNT(*) AS totale,
SUM(t.status IN (1,2,3,4)) AS aperti,
SUM(t.status IN (5,6)) AS chiusi
FROM glpi_tickets t
WHERE t.entities_id IN (SELECT id FROM albero_cliente)
AND t.is_deleted = 0;
Nota il is_deleted = 0: GLPI usa la cancellazione logica e, senza quel filtro, i ticket nel cestino entrano nel conteggio. È la seconda correzione più frequente che applichiamo alle dashboard ereditate da un altro team.
Passo 3 - lo SLA senza gonfiare la percentuale
Il tasso di rispetto dello SLA sembra banale: risolto entro la scadenza, conteggiato. Il dettaglio ingannevole è nel campo time_to_resolve (la scadenza dello SLA salvata sul ticket): è NULL quando nessuno SLA è associato al ticket. Se confronti solvedate <= time_to_resolve senza gestire il NULL, quei ticket spariscono dal denominatore e la percentuale sale artificialmente. Rendi esplicita la popolazione:
WITH RECURSIVE albero_cliente AS (
SELECT id FROM glpi_entities WHERE id = 4
UNION ALL
SELECT e.id FROM glpi_entities e
JOIN albero_cliente a ON e.entities_id = a.id
)
SELECT
COUNT(*) AS con_sla,
SUM(t.solvedate <= t.time_to_resolve) AS nei_tempi,
ROUND(100 * SUM(t.solvedate <= t.time_to_resolve) / COUNT(*), 1) AS pct_sla
FROM glpi_tickets t
WHERE t.entities_id IN (SELECT id FROM albero_cliente)
AND t.is_deleted = 0
AND t.time_to_resolve IS NOT NULL -- solo ticket con SLA
AND t.solvedate IS NOT NULL -- già risolti
AND t.date >= '2026-06-01'
AND t.date < '2026-07-01';
Quali indicatori hanno senso per il cliente
Non tutto ciò che il team interno monitora interessa - o deve essere esposto - al cliente. Un insieme snello funziona meglio:
- Operativo: ticket aperti per stato e priorità, aperti vs. chiusi nel periodo, tempo medio di risposta e di risoluzione.
- SLA: percentuale entro la scadenza e ticket a rischio (vicini alla scadenza di
time_to_resolve). - Soddisfazione: voto medio del sondaggio (
glpi_ticketsatisfactions) e andamento mensile. - Contratto: ore consumate vs. contrattate e saldo residuo, quando c'è un monte ore.
Dove bloccare il filtro: matrice decisionale
Il punto più delicato è impedire che il cliente cambi il parametro nell'URL e veda i dati di un altro. Ogni metodo applica il filtro in un punto diverso:
| Metodo | Dove viene applicato il filtro | Rischio di fuga | Sforzo | Quando usarlo |
|---|---|---|---|---|
| Metabase - Link pubblico | Parametro modificabile nell'URL | Alto | Basso | Solo demo interna, mai con dati del cliente |
| Metabase - Embed firmato (JWT) | Parametro bloccato lato server (token) | Basso | Medio | Opzione consigliata per i clienti |
| Grafana - Accesso anonimo | Espone l'intera organizzazione | Alto | Basso | Da evitare |
| Grafana - Public Dashboards (9.1+) | Una dashboard per link, senza variabile modificabile | Medio | Basso | Cliente singolo, senza sotto-entità sensibili |
| Grafana - organizzazione per cliente | Isolato per org + proxy | Basso | Alto | Molti clienti, isolamento forte |
La trappola che ci è costata di più (nella gestione quotidiana)
In un ambiente con decine di entità, la dashboard di un cliente mostrava circa la metà dei ticket indicati dal report manuale. La causa non era la query di conteggio: era il link pubblico di Metabase. Il filtro per entità era un parametro della dashboard, e chiunque avesse il link poteva modificare il valore nell'URL e navigare in altre entità. Siamo passati a un embed firmato (JWT), con entities_id iniettato nel token e il parametro contrassegnato come locked. Il cliente ha perso la possibilità di toccare il filtro e ha ottenuto un numero che finalmente coincideva con il contratto. La lezione che portiamo in ogni progetto: la sicurezza della dashboard non vive nella query, vive in dove viene applicato il filtro.
Checklist di pubblicazione
- Utente di database in sola lettura, preferibilmente puntato a una replica.
- Filtro per entità che include il sottoalbero (CTE ricorsiva o elenco di ID).
is_deleted = 0in ogni query suglpi_tickets.- Periodo coerente: scegli una data (creazione, risoluzione o chiusura) e usa la stessa su ogni scheda.
- Filtro bloccato lato server (embed firmato), mai solo nell'URL.
- Prova il link con un utente esterno prima di inviarlo al cliente.
Ti servono dashboard cliente con isolamento per entità a prova di manomissione? NexTool implementa e mantiene ambienti GLPI con BI integrata (Metabase e Grafana) e governance dei dati allineata alla normativa sulla protezione dei dati. Scopri il servizio di supporto e manutenzione, oppure consulta il confronto Metabase vs Grafana e i KPI essenziali per il service desk.
Questo contenuto è stato prodotto con l'ausilio dell'intelligenza artificiale e revisionato dal team di Nextool Solutions.