Transparência gera confiança - mas um dashboard mal isolado vira incidente de LGPD. Compartilhar indicadores de atendimento com o cliente reduz cobranças por e-mail e demonstra profissionalismo. O desafio técnico não é desenhar o gráfico: é garantir que cada cliente veja apenas os próprios chamados. Veja como fazer isso com GLPI + Metabase ou Grafana, com o filtro por entidade travado no lugar certo.
Antes de expor: três decisões que definem tudo
Na sustentação de ambientes GLPI multi-cliente, três escolhas decidem se o dashboard será um diferencial ou um problema de segurança:
- Onde travar o filtro por entidade: no link, na query ou no embed assinado. Só o último resiste à adulteração de URL.
- Qual data usar no período: criação (
date), solução (solvedate) ou fechamento (closedate). Misturar as três distorce todos os indicadores. - De onde ler os dados: banco primário ou réplica somente-leitura. Metabase e Grafana disparam consultas pesadas e competem com o GLPI em produção.
Passo 1 - usuário de banco somente-leitura
Nunca aponte a ferramenta de BI para o usuário do GLPI. Crie um usuário dedicado com SELECT e nada além disso. Em MariaDB:
-- Usuário somente-leitura para o BI, restrito à rede interna
CREATE USER 'bi_ro'@'10.20.%' IDENTIFIED BY '...senha_forte...';
GRANT SELECT ON glpi.* TO 'bi_ro'@'10.20.%';
-- Limite de tempo por consulta evita que um dashboard trave o banco (MariaDB)
SET GLOBAL max_statement_time = 15; -- segundos
FLUSH PRIVILEGES;
Se possível, aponte o BI para uma réplica de leitura em vez do banco primário. Assim, um filtro esquecido em um gráfico não segura transações do GLPI em produção - já vimos um único painel com JOIN mal escrito degradar o tempo de resposta do help desk inteiro.
Passo 2 - isolar por entidade sem vazar sub-entidades
Cada cliente é uma entidade no GLPI, e o instinto é filtrar por entities_id = X. Aqui mora a armadilha mais comum: se o cliente tem filiais ou departamentos como sub-entidades, esse filtro captura só a entidade-raiz e os números ficam menores que a realidade. É preciso incluir toda a subárvore. Em MariaDB 10.2.2+ resolve-se com uma CTE recursiva:
-- Todas as entidades do cliente (raiz + descendentes)
WITH RECURSIVE arvore_cliente AS (
SELECT id FROM glpi_entities WHERE id = 4 -- entidade-raiz do cliente
UNION ALL
SELECT e.id
FROM glpi_entities e
JOIN arvore_cliente a ON e.entities_id = a.id
)
SELECT
COUNT(*) AS total,
SUM(t.status IN (1,2,3,4)) AS abertos,
SUM(t.status IN (5,6)) AS encerrados
FROM glpi_tickets t
WHERE t.entities_id IN (SELECT id FROM arvore_cliente)
AND t.is_deleted = 0;
Repare no is_deleted = 0: o GLPI usa exclusão lógica e, sem esse filtro, chamados na lixeira entram na contagem. É o segundo erro que mais corrigimos em dashboards herdados de outra equipe.
Passo 3 - SLA sem inflar o percentual
A taxa de cumprimento de SLA parece trivial: solucionou antes do prazo, contou. O detalhe que engana está no campo time_to_resolve (o prazo de SLA gravado no ticket): ele é NULL quando nenhum SLA está anexado ao chamado. Se você comparar solvedate <= time_to_resolve sem tratar o NULL, esses chamados somem do denominador e o percentual sobe artificialmente. Deixe a população explícita:
WITH RECURSIVE arvore_cliente AS (
SELECT id FROM glpi_entities WHERE id = 4
UNION ALL
SELECT e.id FROM glpi_entities e
JOIN arvore_cliente a ON e.entities_id = a.id
)
SELECT
COUNT(*) AS com_sla,
SUM(t.solvedate <= t.time_to_resolve) AS no_prazo,
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 arvore_cliente)
AND t.is_deleted = 0
AND t.time_to_resolve IS NOT NULL -- só chamados com SLA
AND t.solvedate IS NOT NULL -- já solucionados
AND t.date >= '2026-06-01'
AND t.date < '2026-07-01';
Quais indicadores fazem sentido para o cliente
Nem tudo que a equipe interna acompanha interessa - ou deve ser exposto - ao cliente. Um conjunto enxuto funciona melhor:
- Operacional: chamados abertos por status e prioridade, abertos vs. encerrados no período, tempo médio de resposta e de resolução.
- SLA: percentual dentro do prazo e chamados em risco (próximos do vencimento de
time_to_resolve). - Satisfação: nota média da pesquisa (
glpi_ticketsatisfactions) e tendência mensal. - Contrato: horas consumidas vs. contratadas e saldo, quando há bolsa de horas.
Onde travar o filtro: matriz de decisão
O ponto mais delicado é impedir que o cliente troque o parâmetro na URL e enxergue dados de outro. Cada método resolve o filtro em um lugar diferente:
| Método | Onde o filtro é aplicado | Risco de vazamento | Esforço | Quando usar |
|---|---|---|---|---|
| Metabase - Link público | Parâmetro editável na URL | Alto | Baixo | Só demonstração interna, nunca com dados de cliente |
| Metabase - Embed assinado (JWT) | Parâmetro travado no servidor (token) | Baixo | Médio | Padrão recomendado para cliente |
| Grafana - Acesso anônimo | Expõe toda a organização | Alto | Baixo | Evitar |
| Grafana - Public Dashboards (9.1+) | Um dashboard por link, sem variável editável | Médio | Baixo | Cliente único, sem sub-entidades sensíveis |
| Grafana - organização por cliente | Isolado por org + proxy | Baixo | Alto | Muitos clientes, isolamento forte |
A armadilha que mais custou caro (na sustentação)
Em um ambiente com dezenas de entidades, o dashboard de um cliente exibia cerca de metade dos chamados que o relatório manual apontava. A causa não era a consulta de contagem - era o link público do Metabase. O filtro por entidade estava como parâmetro de dashboard, e qualquer pessoa com o link conseguia editar o valor na URL e navegar por outras entidades. Trocamos por embed assinado (JWT), com o entities_id injetado no token e o parâmetro marcado como locked. O cliente perdeu a capacidade de mexer no filtro e ganhou um número que finalmente batia com o contrato. A lição que levamos para todo projeto: a segurança do dashboard não mora na query, mora em onde o filtro é resolvido.
Checklist de publicação
- Usuário de banco somente-leitura, de preferência apontando para réplica.
- Filtro por entidade incluindo a subárvore (CTE recursiva ou lista de IDs).
is_deleted = 0em toda consulta sobreglpi_tickets.- Período coerente: escolha uma data (criação, solução ou fechamento) e use a mesma em todos os cartões.
- Filtro travado no servidor (embed assinado), nunca apenas na URL.
- Teste do link com um usuário externo antes de enviar ao cliente.
Precisa de dashboards de cliente com isolamento por entidade à prova de vazamento? A NexTool implanta e sustenta ambientes GLPI com BI integrado (Metabase e Grafana) e governança de dados alinhada à LGPD. Conheça o serviço de suporte e sustentação, ou veja o comparativo Metabase vs Grafana e os KPIs essenciais para Service Desk.
Este conteúdo foi produzido com auxílio de inteligência artificial e revisado pela equipe Nextool Solutions.