Mit dem Kunden geteiltes Dashboard in GLPI

So erstellen und teilen Sie Service-Dashboards mit Ihren Kunden mit Metabase oder Grafana auf Basis von GLPI, mit Filter nach Einheit (einschließlich Untereinheiten), korrektem SLA in der Datenbank und manipulationssicherem Zugriff.

In diesem Artikel8 Abschnitte

Transparenz schafft Vertrauen – doch ein schlecht isoliertes Dashboard wird zum LGPD-Vorfall. Servicekennzahlen mit dem Kunden zu teilen reduziert Nachfragen per E-Mail und zeigt Professionalität. Die technische Herausforderung ist nicht, das Diagramm zu zeichnen: Es geht darum sicherzustellen, dass jeder Kunde nur seine eigenen Tickets sieht. So setzen Sie das mit GLPI + Metabase oder Grafana um, mit dem Filter nach Einheit an der richtigen Stelle fixiert.

Vor der Freigabe: drei Entscheidungen, die alles bestimmen

Beim Betrieb von GLPI-Umgebungen mit mehreren Kunden entscheiden drei Weichenstellungen, ob das Dashboard ein Alleinstellungsmerkmal oder ein Sicherheitsproblem wird:

  • Wo der Filter nach Einheit fixiert wird: im Link, in der Abfrage oder im signierten Embed. Nur Letzteres widersteht einer Manipulation der URL.
  • Welches Datum für den Zeitraum gilt: Erstellung (date), Lösung (solvedate) oder Abschluss (closedate). Wer die drei vermischt, verzerrt alle Kennzahlen.
  • Woher die Daten gelesen werden: Primärdatenbank oder schreibgeschützte Replik. Metabase und Grafana feuern schwere Abfragen ab und konkurrieren mit GLPI in der Produktion.

Schritt 1 – schreibgeschützter Datenbankbenutzer

Richten Sie das BI-Werkzeug nie auf den GLPI-Benutzer aus. Legen Sie einen eigenen Benutzer mit SELECT und nichts darüber hinaus an. In MariaDB:

-- Read-only user for BI, restricted to the internal network
CREATE USER 'bi_ro'@'10.20.%' IDENTIFIED BY '...strong_password...';
GRANT SELECT ON glpi.* TO 'bi_ro'@'10.20.%';

-- Per-query time limit keeps a dashboard from stalling the database (MariaDB)
SET GLOBAL max_statement_time = 15;   -- seconds

FLUSH PRIVILEGES;

Wenn möglich, richten Sie das BI auf eine Lese-Replik statt auf die Primärdatenbank aus. So blockiert ein vergessener Filter in einem Diagramm keine GLPI-Transaktionen in der Produktion – wir haben erlebt, wie ein einziges Panel mit schlecht geschriebenem JOIN die Antwortzeit des gesamten Helpdesks verschlechtert hat.

Schritt 2 – nach Einheit isolieren, ohne Untereinheiten zu verlieren

Jeder Kunde ist in GLPI eine Einheit, und der erste Impuls ist, nach entities_id = X zu filtern. Hier liegt die häufigste Falle: Hat der Kunde Filialen oder Abteilungen als Untereinheiten, erfasst dieser Filter nur die Stammeinheit, und die Zahlen fallen kleiner aus als in der Realität. Der gesamte Teilbaum muss einbezogen werden. Ab MariaDB 10.2.2 gelingt das mit einer rekursiven CTE:

-- All of the client's entities (root + descendants)
WITH RECURSIVE client_tree AS (
    SELECT id FROM glpi_entities WHERE id = 4        -- client's root entity
    UNION ALL
    SELECT e.id
    FROM glpi_entities e
    JOIN client_tree c ON e.entities_id = c.id
)
SELECT
    COUNT(*)                       AS total,
    SUM(t.status IN (1,2,3,4))     AS open_tickets,
    SUM(t.status IN (5,6))         AS closed_tickets
FROM glpi_tickets t
WHERE t.entities_id IN (SELECT id FROM client_tree)
  AND t.is_deleted = 0;

Beachten Sie das is_deleted = 0: GLPI verwendet logisches Löschen, und ohne diesen Filter fließen Tickets im Papierkorb in die Zählung ein. Das ist der zweithäufigste Fehler, den wir in Dashboards korrigieren, die wir von einem anderen Team übernehmen.

Schritt 3 – SLA, ohne den Prozentsatz aufzublähen

Die SLA-Erfüllungsquote wirkt trivial: vor der Frist gelöst, gezählt. Das täuschende Detail steckt im Feld time_to_resolve (der im Ticket gespeicherten SLA-Frist): Es ist NULL, wenn dem Ticket kein SLA zugeordnet ist. Vergleichen Sie solvedate <= time_to_resolve, ohne NULL zu behandeln, verschwinden diese Tickets aus dem Nenner, und der Prozentsatz steigt künstlich. Machen Sie die Grundgesamtheit explizit:

WITH RECURSIVE client_tree AS (
    SELECT id FROM glpi_entities WHERE id = 4
    UNION ALL
    SELECT e.id FROM glpi_entities e
    JOIN client_tree c ON e.entities_id = c.id
)
SELECT
    COUNT(*)                                                          AS with_sla,
    SUM(t.solvedate <= t.time_to_resolve)                          AS on_time,
    ROUND(100 * SUM(t.solvedate <= t.time_to_resolve) / COUNT(*), 1) AS sla_pct
FROM glpi_tickets t
WHERE t.entities_id IN (SELECT id FROM client_tree)
  AND t.is_deleted = 0
  AND t.time_to_resolve IS NOT NULL      -- only tickets with an SLA
  AND t.solvedate IS NOT NULL            -- already solved
  AND t.date >= '2026-06-01'
  AND t.date <  '2026-07-01';

Welche Kennzahlen für den Kunden sinnvoll sind

Nicht alles, was das interne Team verfolgt, ist für den Kunden interessant – oder sollte ihm gezeigt werden. Ein schlankes Set funktioniert besser:

  • Operativ: offene Tickets nach Status und Priorität, eröffnete vs. abgeschlossene im Zeitraum, durchschnittliche Reaktions- und Lösungszeit.
  • SLA: Anteil innerhalb der Frist und gefährdete Tickets (kurz vor Ablauf von time_to_resolve).
  • Zufriedenheit: Durchschnittsnote der Umfrage (glpi_ticketsatisfactions) und monatlicher Trend.
  • Vertrag: verbrauchte vs. vertraglich vereinbarte Stunden und Restguthaben, wenn es ein Stundenkontingent gibt.

Wo der Filter fixiert wird: Entscheidungsmatrix

Der heikelste Punkt ist zu verhindern, dass der Kunde den Parameter in der URL ändert und Daten eines anderen sieht. Jede Methode löst den Filter an einer anderen Stelle auf:

MethodeWo der Filter angewendet wirdRisiko eines DatenlecksAufwandWann einsetzen
Metabase – öffentlicher LinkEditierbarer Parameter in der URLHochNiedrigNur für interne Demos, nie mit Kundendaten
Metabase – signierter Embed (JWT)Auf dem Server fixierter Parameter (Token)NiedrigMittelEmpfohlener Standard für Kunden
Grafana – anonymer ZugriffLegt die gesamte Organisation offenHochNiedrigVermeiden
Grafana – Public Dashboards (9.1+)Ein Dashboard pro Link, ohne editierbare VariableMittelNiedrigEinzelner Kunde, ohne sensible Untereinheiten
Grafana – Organisation pro KundeIsoliert per Org + ProxyNiedrigHochViele Kunden, starke Isolation

Die Falle, die am teuersten war (im Betrieb)

In einer Umgebung mit Dutzenden Einheiten zeigte das Dashboard eines Kunden etwa die Hälfte der Tickets, die der manuelle Bericht auswies. Die Ursache war nicht die Zählabfrage – es war der öffentliche Link von Metabase. Der Filter nach Einheit war als Dashboard-Parameter angelegt, und jeder mit dem Link konnte den Wert in der URL ändern und durch andere Einheiten navigieren. Wir haben auf einen signierten Embed (JWT) umgestellt, mit der in das Token injizierten entities_id und dem als locked markierten Parameter. Der Kunde verlor die Möglichkeit, am Filter zu drehen, und erhielt eine Zahl, die endlich mit dem Vertrag übereinstimmte. Die Lehre, die wir in jedes Projekt mitnehmen: Die Sicherheit des Dashboards liegt nicht in der Abfrage, sondern darin, wo der Filter aufgelöst wird.

Checkliste für die Veröffentlichung

  1. Schreibgeschützter Datenbankbenutzer, vorzugsweise auf eine Replik ausgerichtet.
  2. Filter nach Einheit einschließlich des Teilbaums (rekursive CTE oder Liste von IDs).
  3. is_deleted = 0 in jeder Abfrage auf glpi_tickets.
  4. Konsistenter Zeitraum: Wählen Sie ein Datum (Erstellung, Lösung oder Abschluss) und verwenden Sie es in allen Kacheln.
  5. Auf dem Server fixierter Filter (signierter Embed), nie nur in der URL.
  6. Test des Links mit einem externen Benutzer, bevor er an den Kunden geht.

Sie brauchen Kunden-Dashboards mit auslaufsicherer Isolation nach Einheit? NexTool implementiert und betreibt GLPI-Umgebungen mit integriertem BI (Metabase und Grafana) und LGPD-konformer Data Governance. Lernen Sie den Support- und Betriebsservice kennen oder lesen Sie den Vergleich Metabase vs. Grafana und die wesentlichen KPIs für den Service Desk.


Geprüft vom Team von NexTool Solutions.

Häufig gestellte Fragen

Nur nach entities_id = X zu filtern erfasst lediglich die Stammeinheit. Um Filialen und Abteilungen einzubeziehen, bilden Sie den Teilbaum mit einer rekursiven CTE auf glpi_entities (MariaDB 10.2.2+) und verwenden entities_id IN (SELECT id FROM client_tree). Alternativ erzeugen Sie die Liste der IDs in der Anwendung und übergeben sie als Parameter.

Weil time_to_resolve NULL ist, wenn dem Ticket kein SLA zugeordnet ist. Vergleichen Sie solvedate <= time_to_resolve, ohne diese Fälle herauszufiltern, fallen die Tickets ohne SLA aus dem Nenner und blähen den Prozentsatz auf. Grenzen Sie die Grundgesamtheit mit time_to_resolve IS NOT NULL ein.

Nicht für sensible Daten. Beim öffentlichen Link steht der Filter nach Einheit in der URL und kann bearbeitet werden, wodurch Daten anderer Kunden offengelegt werden. Verwenden Sie einen signierten Embed (JWT) mit dem als locked markierten Parameter, der auf dem Server aufgelöst wird. Der öffentliche Link eignet sich nur für interne Demos.

Bevorzugen Sie eine schreibgeschützte Replik. Gibt es keine Replik, legen Sie zumindest einen Benutzer mit GRANT SELECT an und setzen Sie max_statement_time, damit eine schwere Dashboard-Abfrage keine GLPI-Transaktionen in der Produktion blockiert.

Das hängt von der Kennzahl ab. Für das Eingangsvolumen verwenden Sie date (Erstellung). Für Produktivität und SLA verwenden Sie solvedate (Lösung) oder closedate (Abschluss). Der häufige Fehler ist das Vermischen: Gelöste Tickets nach Erstellungsdatum zu zählen unterschätzt das Monatsergebnis.

Vergleichen Sie solvedate mit time_to_resolve im Ticket: SUM(solvedate <= time_to_resolve) über die Gesamtzahl mit SLA. Filtern Sie auf is_deleted = 0, time_to_resolve IS NOT NULL und solvedate IS NOT NULL, damit der Nenner stimmt.

Brauchen Sie Hilfe?