From 28dcfbca946c79a0b03bfa43973d9cb67d5239b2 Mon Sep 17 00:00:00 2001 From: Rodolpho Lopes Date: Tue, 16 Jun 2026 19:56:59 +0000 Subject: [PATCH] KB-PLUGIN-034 + nota menu_toadd no 028 (itemtype de config de plugin) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - KB-PLUGIN-034: cifrar credenciais externas em itemtype via GLPIKey (padrão prepareInput + senha-vazia-mantem-atual). Validado no plugin sync. - KB-PLUGIN-028: seção sobre o mecanismo menu_toadd (seções válidas + estrutura). Co-Authored-By: Claude Fable 5 --- index.json | 19 ++++ ...lpi11-plugin-rights-and-menu-visibility.md | 24 +++++ ...1-encrypt-external-credentials-itemtype.md | 98 +++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 records/plugin-dev/KB-PLUGIN-034-glpi11-encrypt-external-credentials-itemtype.md diff --git a/index.json b/index.json index d3741ad..8900f66 100755 --- a/index.json +++ b/index.json @@ -608,6 +608,25 @@ "severity": "high", "path": "records/plugin-dev/KB-PLUGIN-033-glpi11-plugin-console-commands.md", "summary": "id: KB-PLUGIN-033" + }, + { + "id": "KB-PLUGIN-034", + "title": "\"GLPI 11 — Cifrar credenciais de sistemas externos em itemtype de plugin (GLPIKey)\"", + "domain": "plugin-dev", + "tags": [ + "glpi11", + "plugin", + "security", + "credentials", + "glpikey", + "encryption", + "commondbtm", + "itemtype" + ], + "status": "active", + "severity": "high", + "path": "records/plugin-dev/KB-PLUGIN-034-glpi11-encrypt-external-credentials-itemtype.md", + "summary": "id: KB-PLUGIN-034" } ] } diff --git a/records/plugin-dev/KB-PLUGIN-028-glpi11-plugin-rights-and-menu-visibility.md b/records/plugin-dev/KB-PLUGIN-028-glpi11-plugin-rights-and-menu-visibility.md index 98c73e6..5840a3b 100644 --- a/records/plugin-dev/KB-PLUGIN-028-glpi11-plugin-rights-and-menu-visibility.md +++ b/records/plugin-dev/KB-PLUGIN-028-glpi11-plugin-rights-and-menu-visibility.md @@ -162,6 +162,30 @@ function plugin_meuplugin_install(): bool } ``` +## Mecanismo do menu (menu_toadd) — como o itemtype entra na sidebar + +Registrar o right faz o menu *poder* aparecer; para ele *de fato* aparecer, o +itemtype precisa ser injetado numa seção do menu via o hook `menu_toadd` (lido em +`Html::generateMenuSession()` / `Html.php`): + +```php +// setup.php, dentro de plugin_init_() +Plugin::registerClass('GlpiPlugin\\Meuplugin\\MeuItemtype'); +$PLUGIN_HOOKS['menu_toadd']['meuplugin'] = [ + 'config' => 'GlpiPlugin\\Meuplugin\\MeuItemtype', + // ou vários: 'config' => ['Classe1', 'Classe2'] +]; +``` + +Seções válidas (chave do array): **`assets`, `helpdesk`, `management`, `tools`, +`admin`, `config`** (Setup), `plugins`. Itemtype administrativo de configuração +→ `config`; ver taxonomia em [KB-PLUGIN-029]. + +O `getMenuContent()` herdado de `CommonGLPI`/`CommonDBTM` já monta a entrada +(título, ícone, links search/add) a partir de `getTypeName()`, `getIcon()`, +`getSearchURL()` e `canCreate()` — **gated por `canView()`** (que depende do right). +Não precisa sobrescrever `getMenuContent()` para um CRUD simples. + ## Gotcha — Instalação via CLI `bin/console glpi:plugin:install -f ` roda **sem sessão GLPI**, então `$_SESSION['glpiactiveprofile']['id']` é `null` e `createFirstAccess()` não é executado. Resultado: rights são registrados na tabela mas com valor `0` (sem permissão real). diff --git a/records/plugin-dev/KB-PLUGIN-034-glpi11-encrypt-external-credentials-itemtype.md b/records/plugin-dev/KB-PLUGIN-034-glpi11-encrypt-external-credentials-itemtype.md new file mode 100644 index 0000000..6a9ac9a --- /dev/null +++ b/records/plugin-dev/KB-PLUGIN-034-glpi11-encrypt-external-credentials-itemtype.md @@ -0,0 +1,98 @@ +--- +id: KB-PLUGIN-034 +title: "GLPI 11 — Cifrar credenciais de sistemas externos em itemtype de plugin (GLPIKey)" +domain: plugin-dev +tags: + - glpi11 + - plugin + - security + - credentials + - glpikey + - encryption + - commondbtm + - itemtype +status: active +severity: high +created_at: 2026-06-16 +updated_at: 2026-06-16 +applies_to: + - plugins GLPI 11.x que guardam senha/token de sistema externo + - plugin Sync (conexão com o PROD legado) +related_records: + - KB-PLUGIN-028 + - KB-PLUGIN-032 +--- + +# GLPI 11 — Cifrar credenciais externas em itemtype de plugin + +## Contexto + +Plugins que se conectam a sistemas externos (banco do PROD, API de terceiros, +LDAP secundário) precisam guardar uma senha/token. Guardar em texto puro na +tabela é inaceitável. O GLPI já oferece a mesma cifra que usa para senhas de +e-mail/config: a classe `\GLPIKey` (chave em `GLPI_CONFIG_DIR/glpicrypt.key`). + +## API + +```php +$key = new \GLPIKey(); +$cipher = $key->encrypt($plaintext); // string -> string cifrada +$plain = $key->decrypt($cipher); // string|null -> string|null +``` + +## Padrão no itemtype (CommonDBTM) + +Cifrar no `prepareInputForAdd/Update`, decifrar sob demanda. **Senha vazia no +update = manter a atual** (não sobrescrever com vazio): + +```php +public function prepareInputForAdd($input) { return $this->encryptPasswordInput($input); } +public function prepareInputForUpdate($input) { return $this->encryptPasswordInput($input); } + +private function encryptPasswordInput(array $input): array +{ + if (!array_key_exists('db_password', $input)) { + return $input; + } + if ($input['db_password'] === '' || $input['db_password'] === null) { + unset($input['db_password']); // mantém a senha atual + return $input; + } + $input['db_password'] = (new \GLPIKey())->encrypt((string) $input['db_password']); + return $input; +} + +public function getDecryptedPassword(): string +{ + $enc = (string) ($this->fields['db_password'] ?? ''); + return $enc === '' ? '' : (string) (new \GLPIKey())->decrypt($enc); +} +``` + +No form Twig, o campo de senha deve renderizar **sempre vazio** (nunca ecoar a +cifra) e avisar que vazio mantém a atual: + +```twig +{{ fields.passwordField('db_password', '', __('Senha', 'sync'), + {'helper': __('Deixe em branco para manter a senha atual', 'sync')}) }} +``` + +## Gotchas + +- A coluna do banco guarda a **cifra** (use `TEXT`/`VARCHAR` amplo; a cifra é + maior que o texto puro). +- `decrypt()` pode retornar `null` — sempre fazer cast/checagem. +- Não logar nem expor a cifra em search options visíveis ao usuário comum. +- Cuidado se a `glpicrypt.key` mudar entre ambientes: cifra feita num GLPI não + decifra noutro com chave diferente (relevante em migração/cutover). + +## Validação + +Comprovado no plugin `sync` (2026-06-16): teste E2E criou Source com senha, +confirmou que `db_password` em repouso ≠ texto puro, que `getDecryptedPassword()` +devolve o original, e que update com senha vazia preserva a cifra anterior. + +## Classificação + +- Tipo: padrão de segurança reutilizável. +- Reutilização: qualquer plugin que persista credencial de sistema externo.