From b3c0fd2c29e966d8fd35139852c601de4a70956d Mon Sep 17 00:00:00 2001 From: test Date: Mon, 31 Aug 2026 10:10:30 +0200 Subject: [PATCH] fix logs client --- MEMORY.md | 12 ++++++++++++ README.md | 5 +++++ front/dropdown_host.ajax.php | 6 ++++++ front/server_test.ajax.php | 16 +++++++--------- locales/de_DE.mo | Bin 14280 -> 14504 bytes locales/de_DE.po | 7 +++++++ locales/en_GB.mo | Bin 13347 -> 13539 bytes locales/en_GB.po | 7 +++++++ locales/it_IT.mo | Bin 14019 -> 14227 bytes locales/it_IT.po | 7 +++++++ public/js/urbackup.js | 31 +++++++++++++++++++++++++++++++ setup.php | 4 ++++ src/AssetTab.php | 35 ++++++++++++++++++++++++++++++++--- src/Server.php | 18 ++++++++++++------ 14 files changed, 130 insertions(+), 18 deletions(-) create mode 100644 public/js/urbackup.js diff --git a/MEMORY.md b/MEMORY.md index 731fa6b..ee019f8 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -2,6 +2,18 @@ ## Ultima modifica: 31/08/2026 +## 31/08/2026 — Audit statico: fix 7 bug (solo UI/codice, nessuna modifica DB) +Audit statico (IA) + confronto contro sorgente C++ del backend UrBackup e core GLPI 11. Nessun bug di classe critico (crash/XSS/SQLi), ma diversi output errati corretti. +1. **Bug 1 — Livello log sempre vuoto** (`AssetTab.php:728`): l'API `livelog` restituisce `loglevel` (int 1=ERROR,2=WARNING,3=INFO,4=DEBUG), il codice leggeva `level`/`severity` (mai presenti). Aggiunto `formatLogLevel()` che mappa `loglevel`→stringa. +2. **Bug 2 — "No client logs available" fuorviante** (`AssetTab.php:733`): se `client_found=false` il client non è stato trovato per nome e i log NON vengono mai estratti. Ora mostra avviso "Client not found on UrBackup server. Check that the asset name matches the client name." Il matching resta per NOME client (confermato dall'utente come corretto). +3. **Bug 3 — JOIN IP invertito** (`Server.php` `batchLoadIps` ~1516 e `getAssetIp` ~1614): JOIN glpi_ipaddresses↔glpi_networknames generava `nn.items_id = ipa.id`; corretto a `nn.id = ipa.items_id` (con `ipa.itemtype='NetworkName'`), coerente con core `glpi/src/Report.php`. Colonna IP di Linked/Missing ora corretta. +4. **Bug 4 — Chiave cache sessione non univoca** (`AssetTab.php:374`): `$cache_key` includeva solo server id+nome client; ora `urbackup_data_{serverid}_{itemtype}_{items_id}`, evita cross-contaminazione tra asset omonimi. +5. **Bug 5 — Query Missing/Unlinked non filtrate per server** (`Server.php:1047` e `:1245`): aggiunto `WHERE plugin_urbackup_servers_id = server corrente` in `showUnlinkedClientsTab()` e `showMissingClientsTab()`, così asset collegati ad altri server non interferiscono. +6. **Bug 6 — server_test.ajax.php auth + CSRF** (`front/server_test.ajax.php`): sostituito `Profile::canCurrentUser(UPDATE)` (non entity-aware) con `$server->check($id, UPDATE)` in try/catch → 403 JSON; creato `public/js/urbackup.js` (registrato via `Hooks::ADD_JAVASCRIPT` in setup.php) che legge meta `glpi:csrf_token` e invia header `X-Glpi-Csrf-Token` su ogni POST AJAX del plugin (pre-requisito del listener GLPI 11 `CheckCsrfListener`). +7. **Bug 7 — dropdown_host.ajax.php info-disclosure** (`front/dropdown_host.ajax.php`): aggiunto check `Profile::canCurrentUser(READ)`. +8. **Traduzioni**: aggiunta la nuova stringa "Client not found on UrBackup server. Check that the asset name matches the client name." a it_IT/de_DE/en_GB `.po`, ricompilati i `.mo` (msgfmt). Versione header coerente (0.7.3). Changelog README aggiornato. +- Verifica IA: `php -l` OK su tutti i file; `git diff` autorevisione OK; `Hooks::ADD_JAVASCRIPT` verificato in `src/Glpi/Plugin/Hooks.php:60`; meta `glpi:csrf_token` verificato in `templates/layout/parts/head.html.twig:68`. **Verifica UI utente OBBLIGATORIA** (vedi checklist). + ## 31/08/2026 — Fix campi API username/password non editabili in prod (solo UI) - **Sintomo utente**: installato il plugin sul server produttivo, nel form Server non si vede il campo API username e il campo API password è bloccato (asterischi fissi non editabili), anche per l'utente che ha installato il plugin. - **Causa**: divergenza nel controllo diritti. I front usavano `Profile::canCurrentUser()` (legge i diritti da `glpi_profilerights` su DB), ma `Server::showFormFields()` (form server) usava `Session::haveRight(self::$rightname, UPDATE)` per calcolare `$canEdit` (e idem `rawSearchOptions()` riga 308 e `showUnlinkedClientsTab()`/`showMissingClientsTab()` righe 1155/1222). `Session::haveRight` dipende dalla **cache dei diritti in sessione** (`$_SESSION['glpiactiveprofile']['rights']`), popolata al login. Dopo l'installazione/aggiornamento del plugin i diritti sono scritti in DB ma la **sessione corrente non viene ricostruita**, quindi `haveRight` resta false → ramo read-only: username stampato come testo (invisibile se vuoto), password `******` fissa. diff --git a/README.md b/README.md index 7111280..46d2846 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,11 @@ plugin_urbackup/ - The asset tab now shows a "This asset hosts the UrBackup server" block with links to the hosted servers (always visible, even when the asset is also a linked client) - Server save validates the host link (unknown/disabled itemtype or missing item resets the link) - Added it_IT/de_DE/en_GB translations (2 new strings) and recompiled locales +- Fixed the plugin right check on the server form and internal tabs (`Session::haveRight` → `Profile::canCurrentUser`), so API username/password become editable right after plugin installation without re-login +- Client logs: fixed the log level column (API returns `loglevel`, now mapped to ERROR/WARNING/INFO/DEBUG); show a clear warning when the client is not found by name instead of the misleading "No client logs available" +- Fixed the inverted IP lookup JOIN (`glpi_networknames` ↔ `glpi_ipaddresses`) so the IP column in Linked/Missing clients is correct +- Made the missing/unlinked clients queries server-scoped (`plugin_urbackup_servers_id`) and the API data session cache key unique per asset (itemtype+id) +- `server_test.ajax.php`: entity-aware `check()` authorization; added `public/js/urbackup.js` (registered via `ADD_JAVASCRIPT`) that sends the `X-Glpi-Csrf-Token` header on plugin AJAX POSTs; `dropdown_host.ajax.php` now requires READ right ### 0.7.2 - UrBackup on Computer is now configurable: new `enable_computer` setting on the plugin configuration page (previously always enabled, hardcoded) diff --git a/front/dropdown_host.ajax.php b/front/dropdown_host.ajax.php index 7fd62d9..a951362 100644 --- a/front/dropdown_host.ajax.php +++ b/front/dropdown_host.ajax.php @@ -11,6 +11,7 @@ declare(strict_types=1); */ use GlpiPlugin\Urbackup\Config; +use GlpiPlugin\Urbackup\Profile; if (!defined('GLPI_ROOT')) { define('GLPI_ROOT', dirname(__DIR__, 4)); @@ -22,6 +23,11 @@ Html::header_nocache(); Session::checkLoginUser(); +if (!Profile::canCurrentUser(READ)) { + http_response_code(403); + exit; +} + if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'GET') { http_response_code(405); exit; diff --git a/front/server_test.ajax.php b/front/server_test.ajax.php index e727dd2..642d526 100644 --- a/front/server_test.ajax.php +++ b/front/server_test.ajax.php @@ -10,7 +10,6 @@ declare(strict_types=1); * only accepts POST requests (GET requests must not trigger state changes). */ -use GlpiPlugin\Urbackup\Profile; use GlpiPlugin\Urbackup\Server; use GlpiPlugin\Urbackup\UrbackupApiClient; @@ -31,12 +30,6 @@ if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { exit; } -if (!Profile::canCurrentUser(UPDATE)) { - http_response_code(403); - echo json_encode(['success' => false, 'message' => __('No permission', 'urbackup')]); - exit; -} - $server_id = (int) ($_POST['id'] ?? 0); if ($server_id <= 0) { @@ -46,8 +39,13 @@ if ($server_id <= 0) { $server = new Server(); -if (!$server->getFromDB($server_id)) { - echo json_encode(['success' => false, 'message' => __('Server not found', 'urbackup')]); +try { + // Entity-aware authorization: checks UPDATE right AND access to the server's entity + // (returning the server on success, throwing otherwise). + $server->check($server_id, UPDATE); +} catch (Throwable $e) { + http_response_code(403); + echo json_encode(['success' => false, 'message' => $e->getMessage()]); exit; } diff --git a/locales/de_DE.mo b/locales/de_DE.mo index c61d79ca2cd32b3b574ae79e3625a5b271bde335..30b89fbed6b8259a51ba2e76720a9c05e7336f21 100644 GIT binary patch delta 4169 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 J|NrRe{{SV(S(5+& diff --git a/locales/en_GB.po b/locales/en_GB.po index 6e8442e..8b0bf11 100644 --- a/locales/en_GB.po +++ b/locales/en_GB.po @@ -101,6 +101,13 @@ msgstr "" msgid "Client not found on UrBackup server." msgstr "Client not found on UrBackup server." +msgid "" +"Client not found on UrBackup server. Check that the asset name matches the " +"client name." +msgstr "" +"Client not found on UrBackup server. Check that the asset name matches the " +"client name." + msgid "Client state" msgstr "Client state" diff --git a/locales/it_IT.mo b/locales/it_IT.mo index 15c77b7912d18549e03ab6d0066dd5fd18b51b9b..ae65a1923db49aa05f313995c8e5d3d760937c07 100644 GIT binary patch delta 4151 zcmY+{cW_ii0LSqq2?>yd6uPtrgoF}$C=rkvdXXAH6F9;FffPtWFOPs!0l^pyAc9gt zi2_DCI5d?J29;q%{=hOybp%nyaRkxt?`|1&hu?j6-`%~f?|^fe1=;R5(LsfVa*Wg> zKbAG-F_wh#LMe(cCKfkhUp$TFu|lLVQCJzPU@UgTPPRQ8!)UL?3b@&}cVP(aQVcf6 zH7BT4;=)-}2VY@DbhtZXF)gqGw#7^wjr=j6+UwU*1NsXYn+b{bTMcW|jzbM32{oso z)>Q0C|7Hr67~a^A$#@m@g?L7#fh1x%>|-5*m1&Pb%_JL};Vi6*JFy9#L}qAiV-38I zjj$YF4#!p)P5-6~mF74U>tj9!;SS_GW)JE{r|kRZQA>IowGzK#3k+f(sG0<8U#v+x z6%%kS>OT8177wFahsq5q>i7xj4Af;BTB=Ss4*MXxV)mdqJdPURCDayuiJH+h)WB|H zHT(s&lI3XVepOJ{>+w<@6RWZQ`od5yXrw8q8_hv>!7N90xCu3Y64XFXp|<8b)Sf>; zb?`UpbD`Xws;Pzgegdk)UcBgYW3e$#t2GDMY8dxHB!+!W2u6C(t$%|2^_&oB*T;@fm{tmXqFg~pt zbjLUxh8plZ+g^zp&_RsH^Qev=qHn;cy?=tbPaw;!6>=+6X-Op>b;?t$6HzOZjhfjE z)XL074Rj^yh6Pv;H=#av(!PJjzJC$b;T8M-4>*(d9e>+3W0}52JOOpX^{5dSVG3@; z(fAbgxlugSF}M`#;$C#{Gt|obgc?XCPIfP>j@r7h$mGom)cv<(q@MqMRMhcF)C!zK z`ZAYLGrNoGD3q6m*c6B35Y)_eAqU+YK@IRS@*tVV_Ii}#U+PAv`*uZcX;Lu4r81w2 zMz$KY_r<8aIfLrpI_i+!Lv{2gHpM7Lrwe_HFo$s@evX<*Ec>Q2 z)gHByonu)41ys_wppL)CX7~uzL47`@Ll=+gs3SJU{-}Y>Kn;8`>bYKngK;D3EZoIO z7{zq;`8lZjEJJPehFI2Lm91RRjCZ4!^fb1{3+UTRY)(6hy9Qxb)aR41J*J?3XbMpi zDMo$%xb-gTd(}CKx?gP!!8R@xR@-z$%^($ZgDKb+7oe816m>=}AqU*t#=cmV<h;DM;`FGGEQH6~*rPQssCvHq-< z8NtT!gqbC%CESa8DlQ?%z&t>0Ma?$+?4pC*!{nd_x&uS;JZcNSKy`S_`T%uy{y=R- z6_!``Pe@?>S#^`l1&wGKM&NzaQawe!W2*8j>hbGm^`hQigc@)W>ajbH`u?}54j-Vl zB#iw~I~MhR57f#JbE)X@8IKysJk-edqL#801Mws5foCxZgWLOm+XrA7+Ui7QAJJLq zPF^Ex$YG*;X#l&34xvgvqVwk~TF%8}714}T_K?}cL!KuYMEg0IJS*8$H1J{)O>{mc zlC>m_tRN0isY>>eF{BcSQ~x}MzVb4!$87x>)2JLGA>;_zMpSZ$mQh99u$p9&okV4v z@70)Gt9YOEB%8=v$YzqIhAmx? zhr#qGeMmZakMt!fACPbosr7G2CXhn1g$yTp8g%fAh)Oci3E!xOEsLx=xC==s(ch*j zoymN%l8h$L$}3bF5IsdI1y{o(T|Ei~zNY_i3ZTxp^8F~y!FS1avYrGH{^{oXC)G&& zZz)fsHPNKDl8z*isPG_|xUyc7( z;XGTPih-mG*Q7j;W3Q`eV&=y5%g(_|ZcTwS?s4Ob2tgTWv zY?Y#dV!<|Qnt-G>T0_KWj1tteMvM(HrI>1(e*erh;f8;{bMAfb>~}_2&e?ulZBNH$ ztBdPp`M!L3SSjCd-4TAcmb5En0GDziH?tN0%MNVTzLYNP%8@*-s?TO?^#wef_gD2b zY@xoM%}c43jS8LIc#-K~C)={U){Ujqu`m1cI!>g2Wm|Rs4Q4=J(wNG@$`&0<>8ak0 z8OU&EPU9*k^9=o$ixv8L@EG&K9n1iBu_gCbe$LM7UokUiVbsU79lLTgkL6UFS*fu* z@8dCioNf39HBsK<3HmSl6n?<&T5rZtbgPVI8oH2qKAl<08cnh+<6y3%f91`}k9m~( ze|RE0+Ml#Dfde?1y?8TgX?T@F_I?|)M7wzz_tI{Zv8I;}&u0cWi`km1m>JDs1~#AF zxP)2B$C>t?tM31aN2>2)KEJQ4_0LETxRHiB_;!}2j_I(T8NeCLKrdvr<~C-p7c(8K zWj?o_{*{;f$oJo5I^4s2?jQ%UshgR=@kd(!bUe(BG(486pUcd22D5iFnLU4i8Q>~r z08cZY+s4fJm8$**o2dVt8Q3n41Uyp$t2jcb^h z?Povs^itPx9DDO}W+fIe1KG&&+{|p^)kEjDOkoCi1-&q3Wp#f8v($fK+I^qCS`IL0r*m%ut10wTu&reb z^Mz?l2iG$nyqoE01qX2hGn03j8Gpp4{De7V|72F+7_%6`IlP3=Z~&Wml`@e*tmQpi zsE|W+KQn+wIf+}D88vkxTeCZJ1_m$#8^UbeX_XV`fS1Xdd z+ss7Y?{EFHl>6PN=hw`P2U({RIF`*gm-)iYJedzNzhGWxCh#8f`F)k$jUwMWi)rs1 zrk|O#sO1`F;LB?YY2aZF=X1;we#)HMZ|MM+ZjSOq4rdG2=uufri&8dmFn9AfHnUvm zU?}HuG&}Qorv2^A0BavA9H#Iw^NZy(X6X(ve}tMG+qi^Xnfn8n28J=GdJ+%kRdl|} zP0WPWFdc5Fe2rPLcbI|ygW9i^HjZ=-Ngrka^-Ke&F&$jMoc_7&&BaW^&#<1aGApv5 z+3T;Fj#^rVwA+KtIfx#WdbZ*WcFFrcTOnUuz%0pfW*{qhIGKFbVb6DM;gGvh8! zt^-|8W@}!;yyvrN5zAtp!WWqDf5B{369=YU-v4@q6FH9gJ#Z^W@FAw-zcDj>i2N2rC2v*r zypZWe)=KHVk$9$bvvggpo*Zf|3QOcH~);~-5fg5=(zGe2P&c
P}9`e&(5 zpl4+~^ZMOhxw?A3nHlh3nb&R~^Zj<--E>&TY{{sqei8HhMrP?3F>lWbW*{4D3R$WT zn5F!bP5CdL&aZeTPqM+|c_+Webdu{2l3z$;Ww|^cf0VS90sT(=N@%zyRt7s#?vmP4 zKYB~9XXIA7Oiqz&B>Op8zPo;*-~gB3OGnB1__;hNm&;E*othhB43UXq;Ft@4=U+9FxTT-m1kiTZ2u29dP&lly+>e(pXAxq>Z)xEGta`Mkz`B9s0 zwLVHKB-i!QL>#Dwqj(#ikSFC=lK1Z@Ia)51L$6jULuG+HDnFDFlFJ)W?vsnn=3iKD&NCxRrz_|UX`zAQ|ThBt9x@RFXg1F+(35zY`Ixl gN-vo%S4wBOQO0ap-EMr-EyFtP>9}RyfF2$G4@+ThSpWb4 diff --git a/locales/it_IT.po b/locales/it_IT.po index 3bfe06d..26fc430 100644 --- a/locales/it_IT.po +++ b/locales/it_IT.po @@ -105,6 +105,13 @@ msgstr "" msgid "Client not found on UrBackup server." msgstr "Client non trovato sul server UrBackup." +msgid "" +"Client not found on UrBackup server. Check that the asset name matches the " +"client name." +msgstr "" +"Client non trovato sul server UrBackup. Verifica che il nome dell'asset " +"corrisponda al nome del client." + msgid "Client state" msgstr "Stato client" diff --git a/public/js/urbackup.js b/public/js/urbackup.js new file mode 100644 index 0000000..efad1e9 --- /dev/null +++ b/public/js/urbackup.js @@ -0,0 +1,31 @@ +(function ($, document) { + 'use strict'; + + function getCsrfToken() { + var meta = document.querySelector('meta[property="glpi:csrf_token"]'); + return meta ? meta.getAttribute('content') : ''; + } + + // GLPI 11 validates the CSRF token of every AJAX POST through the + // X-Glpi-Csrf-Token header (see CheckCsrfListener). Register the token + // on all plugin AJAX requests so endpoints such as server_test.ajax.php + // are accepted instead of being rejected with a 403. + $(document).on('ajaxSend', function (event, xhr, settings) { + var token = getCsrfToken(); + if (token !== '' && typeof settings !== 'undefined' && settings.type && settings.type.toUpperCase() === 'POST') { + xhr.setRequestHeader('X-Glpi-Csrf-Token', token); + } + }); + + // Also cover direct $.ajax/{$.get[Script]/-free POST} + var origAjax = $.ajax; + $.ajax = function (url, options) { + var settings = $.isPlainObject(url) ? url : $.extend({ url: url }, options || {}); + var token = getCsrfToken(); + if (settings.type && settings.type.toUpperCase() === 'POST' && token !== '') { + settings.headers = settings.headers || {}; + settings.headers['X-Glpi-Csrf-Token'] = token; + } + return origAjax.call(this, settings); + }; +})(jQuery, document); diff --git a/setup.php b/setup.php index 2790ed4..cc99d6d 100644 --- a/setup.php +++ b/setup.php @@ -93,6 +93,10 @@ function plugin_init_urbackup(): void $PLUGIN_HOOKS[Hooks::ADD_CSS]['urbackup'] = [ 'public/css/urbackup.css', ]; + + $PLUGIN_HOOKS[Hooks::ADD_JAVASCRIPT]['urbackup'] = [ + 'public/js/urbackup.js', + ]; } /** diff --git a/src/AssetTab.php b/src/AssetTab.php index 6de1df9..41da905 100644 --- a/src/AssetTab.php +++ b/src/AssetTab.php @@ -371,7 +371,7 @@ class AssetTab extends CommonDBTM $client_name = (string) ($item->fields['name'] ?? ''); $asset_ip = ServerAsset::extractAssetIp($item); - $cache_key = 'urbackup_data_' . $server->fields['id'] . '_' . $client_name; + $cache_key = 'urbackup_data_' . $server->fields['id'] . '_' . $item::class . '_' . $item->fields['id']; if (isset($_SESSION[$cache_key]) && $_SESSION[$cache_key]['time'] > time() - 30) { return $_SESSION[$cache_key]['data']; } @@ -725,14 +725,18 @@ class AssetTab extends CommonDBTM foreach ($logs as $log) { echo ""; echo "" . htmlspecialchars(self::formatTimestamp($log['time'] ?? $log['created'] ?? '')) . ""; - echo "" . htmlspecialchars((string) ($log['level'] ?? $log['severity'] ?? '')) . ""; + echo "" . htmlspecialchars(self::formatLogLevel($log)) . ""; echo "" . htmlspecialchars((string) ($log['message'] ?? $log['msg'] ?? $log['text'] ?? '')) . ""; echo ""; } if (count($api_data['logs']) === 0) { echo ""; - echo htmlspecialchars(__('No client logs available.', 'urbackup')); + if ($api_data['client_found'] !== false) { + echo htmlspecialchars(__('No client logs available.', 'urbackup')); + } else { + echo htmlspecialchars(__('Client not found on UrBackup server. Check that the asset name matches the client name.', 'urbackup')); + } echo ""; } @@ -931,6 +935,31 @@ class AssetTab extends CommonDBTM return $str; } + /** + * Format the numeric log level returned by the UrBackup "livelog" API + * into a readable label. + * + * @param array $log Log entry row + * + * @return string + */ + private static function formatLogLevel(array $log): string + { + $raw = (string) ($log['loglevel'] ?? $log['level'] ?? $log['severity'] ?? ''); + + if ($raw === '') { + return ''; + } + + return match ((int) $raw) { + 1 => 'ERROR', + 2 => 'WARNING', + 3 => 'INFO', + 4 => 'DEBUG', + default => is_numeric($raw) ? $raw : ucfirst(strtolower($raw)), + }; + } + public static function startBackup(CommonDBTM $item, string $type): bool { $link = ServerAsset::getLinkForAsset($item::class, (int) $item->fields['id'], false); diff --git a/src/Server.php b/src/Server.php index f54da51..01cde7a 100644 --- a/src/Server.php +++ b/src/Server.php @@ -1045,7 +1045,10 @@ class Server extends CommonDBTM global $DB; $iterator = $DB->request([ - 'FROM' => 'glpi_plugin_urbackup_serverassets', + 'FROM' => 'glpi_plugin_urbackup_serverassets', + 'WHERE' => [ + 'plugin_urbackup_servers_id' => (int) $server->fields['id'], + ], ]); // Batch-load asset names (one query per itemtype) to avoid N+1 lookups. @@ -1240,7 +1243,10 @@ class Server extends CommonDBTM $rootLocationId = LocationHelper::getRootLocationId($serverLocationId); $linkedIterator = $DB->request([ - 'FROM' => ServerAsset::getTable(), + 'FROM' => ServerAsset::getTable(), + 'WHERE' => [ + 'plugin_urbackup_servers_id' => (int) $server->fields['id'], + ], ]); $linkedAssetKeys = []; foreach ($linkedIterator as $row) { @@ -1515,8 +1521,8 @@ JAVASCRIPT; 'INNER JOIN' => [ 'glpi_networknames AS nn' => [ 'ON' => [ - 'nn' => 'items_id', - 'ipa' => 'id', + 'nn' => 'id', + 'ipa' => 'items_id', ['AND' => ['ipa.itemtype' => 'NetworkName']], ], ], @@ -1613,8 +1619,8 @@ JAVASCRIPT; 'INNER JOIN' => [ 'glpi_networknames AS nn' => [ 'ON' => [ - 'nn' => 'items_id', - 'ipa' => 'id', + 'nn' => 'id', + 'ipa' => 'items_id', ['AND' => ['ipa.itemtype' => 'NetworkName']], ], ],