Skip to content

"Metabase 1.4.2 breaks on GLPI 11: unhandled API renames break extraction, and a broken migration breaks the embedded dashboard entirely" #148

Description

@whamulti

Code of Conduct

  • I agree to follow this project's Code of Conduct

Is there an existing issue for this?

  • I have searched the existing issues

GLPI Version

11.0.8

Plugin version

1.4.2

Bug description

Dashboard/question extraction is broken against current Metabase API versions. Several response fields the plugin reads under their old names were renamed by Metabase, and a newer native-question format ("MBQL 5") isn't handled at all. As a result:

  • PluginMetabaseAPIClient::getDashboardCards() returns false for every dashboard (reads ordered_cards, which no longer exists — the API now returns dashcards).
  • PluginMetabaseAPIClient::getCards('root') never returns questions that are in the root collection (the API returns id: "root" for that collection, but cards in it have collection_id: null — the strict === comparison never matches).
  • Extracting a native question created with a current Metabase produces empty/null SQL and no template tags, because dataset_query.native.query / dataset_query.native.template-tags moved to dataset_query.stages[0].native / dataset_query.stages[0].template-tags in current Metabase versions.
  • Card sizes come back empty in dashboard JSON extraction (sizeX/sizeY renamed to size_x/size_y).
  • The "Extract" button in the collection list does nothing when GLPI's root_doc is empty (default install) — public/metabase.js builds the AJAX URL as root_doc + '/' + GLPI_PLUGINS_PATH.metabase + ..., which becomes //plugins/... and the browser resolves it as a request to a host named plugins instead of a path.

I expected the "Extract questions" feature and dashboard card extraction to work against a current Metabase instance the same way they do with an older one. Instead they silently return nothing (no PHP error, no exception — the methods just return false/empty), which makes it look like there's simply nothing to extract.

I confirmed this is a real, reproducible bug (not a config/environment issue) by calling the affected methods directly against a live Metabase instance with real dashboards, both before and after patching — details and suggested fix below.

Second, more severe bug, also introduced in 1.4.2: upgrading a site that already had an embedded_token configured breaks the embedded dashboard on the Central page entirely with an uncaught exception (Lcobucci\JWT\Signer\InvalidKeyProvided: "Key cannot be empty"). 1.4.2 added embedded_token to secured_configs (setup.php) and expects it to be sodium-encrypted, but the migration meant to encrypt existing plain-text tokens on upgrade doesn't actually do that — see config.class.php::install():

// Encrypt embedded_token, previously stored in plain text
if (!array_key_exists('is_embedded_token_encrypted', $current_config) || !$current_config['is_embedded_token_encrypted']) {
    if (!empty($current_config['embedded_token'])) {
        Config::setConfigurationValues('plugin:metabase', [
            'embedded_token' => $current_config['embedded_token'],   // just rewrites the same plain-text value, never actually encrypts it
        ]);
    }
    Config::setConfigurationValues('plugin:metabase', ['is_embedded_token_encrypted' => 1]);  // ...but marks it as encrypted anyway
}

dashboard.class.php then unconditionally does (new GLPIKey())->decrypt($config['embedded_token']) — decrypting a value that was never actually encrypted returns an empty string, which the JWT signer rejects. Any site that had embedded_token set before upgrading to 1.4.2 hits this immediately on the Central dashboard tab (fresh installs without a pre-existing token aren't affected).

Relevant log output

For the extraction bugs: none — no PHP error or exception, the affected methods simply return false/empty arrays.

For the embedded-dashboard bug:

[2026-08-05 20:44:23] glpi.CRITICAL: *** Uncaught PHP Exception Lcobucci\JWT\Signer\InvalidKeyProvided: "Key cannot be empty" at InvalidKeyProvided.php line 39

### Page URL

Setup > Metabase > (collection extraction dialog / dashboard "Extract questions" button) — not a public-facing page, it's the plugin's own admin UI.

### Steps To reproduce

**Extraction bugs:**
1. With Metabase plugin 1.4.2 configured against a current Metabase instance (reproduced on both v0.62 and v0.63.2)
2. Create a native SQL question and put it on a dashboard in Metabase (or use one in the root collection)
3. In GLPI, go to Setup > Metabase and try to extract that dashboard's questions / the root collection's questions
4. See empty result — no cards/questions come back, no error is shown anywhere

**Embedded dashboard bug:**
1. Have Metabase plugin working on an older version with `embedded_token` already configured (plain text, pre-`1.4.2`)
2. Update the plugin to `1.4.2`
3. Go to GLPI Central, open the "Painel Metabase" / embedded dashboard tab
4. See "Ocorreu um erro inesperado" / uncaught `InvalidKeyProvided` exception in `php-errors.log`


### Your GLPI setup information

GLPI version: 11.0.8
PHP version: 8.2.32 (cli, NTS)
Web server: Apache/2.4.68 (Debian)
Database: MariaDB 10.11.14

(Full System tab output from Setup > General > System can be attached if needed.)

Anything else?

Suggested fix — simple ?? fallbacks, kept backward-compatible with older Metabase versions:

// apiclient.class.php::getDashboardCards()
public function getDashboardCards($id)
{
    $data = $this->httpQuery("dashboard/$id", [], 'GET');
    return $data['dashcards'] ?? $data['ordered_cards'] ?? false;
}
// apiclient.class.php::getCards()
$normalized_id = $collection_id === 'root' ? null : $collection_id;
$cards = array_filter($cards, fn($card) => is_array($card)
    && array_key_exists('collection_id', $card)
    && $normalized_id === $card['collection_id']);
// config.class.php, wherever dataset_query.native.* / card sizes are read
$sql = $card['dataset_query']['native']['query']
    ?? $card['dataset_query']['stages'][0]['native'];
$size_x = $card['sizeX'] ?? $card['size_x'];

For the embedded-dashboard bug, the migration needs to actually encrypt the value instead of just flipping the flag, e.g.:

// config.class.php::install(), embedded_token migration block
if (!empty($current_config['embedded_token'])) {
    $current_config['embedded_token'] = (new GLPIKey())->encrypt($current_config['embedded_token']);
    Config::setConfigurationValues('plugin:metabase', [
        'embedded_token' => $current_config['embedded_token'],
    ]);
}

(mirrors what the password migration a few lines above already does correctly.)

I have a working patch applying all of this cleanly on top of 1.4.2 (tested end-to-end against a live Metabase v0.63.2 instance with real dashboards — both the extraction features and the embedded dashboard) if it's useful as a starting point for a PR — happy to open one if that's welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions