diff --git a/library/Icingadb/Common/SearchControls.php b/library/Icingadb/Common/SearchControls.php index a352ed2d7..aeecfd587 100644 --- a/library/Icingadb/Common/SearchControls.php +++ b/library/Icingadb/Common/SearchControls.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Icingadb\Common; +use Icinga\Module\Icingadb\Hook\SearchColumnsProviderHook; use ipl\Html\Html; use ipl\Orm\Query; use ipl\Web\Control\SearchBar; @@ -35,9 +36,21 @@ public function createSearchBar(Query $query, ...$params): SearchBar $searchBar->addWrapper(Html::tag('div', ['class' => 'search-controls'])); } + $model = $query->getModel(); + $defaultColumns = $model->getSearchColumns(); + $columns = SearchColumnsProviderHook::getCustomVarColumns($model, $defaultColumns); + $searchBar->setSearchColumns($columns); + + $searchBar->handleRequest($this->getServerRequest()); + return $searchBar; } + private function callHandleRequest() + { + return false; + } + /** * Create and return a ContinueWith * diff --git a/library/Icingadb/Hook/SearchColumnsProviderHook.php b/library/Icingadb/Hook/SearchColumnsProviderHook.php new file mode 100644 index 000000000..615d48a04 --- /dev/null +++ b/library/Icingadb/Hook/SearchColumnsProviderHook.php @@ -0,0 +1,52 @@ + +// SPDX-License-Identifier: GPL-3.0-or-later + +namespace Icinga\Module\Icingadb\Hook; + +use Icinga\Application\Hook; +use Icinga\Application\Logger; +use ipl\Orm\Model; +use Throwable; + +/** + * Hook for providing additional search columns such as custom variables. + * + * This is a first attempt to generalize support for modules to provide additional search columns. + * The end user won't be able to customize the set provided and every global or route search will + * always use them. Any hook implementation must ensure that only columns the user has access to + * and that are valid in the current route are returned. Returning invalid columns may cause errors + * for users. See https://github.com/Icinga/icingadb-web/security/advisories/GHSA-w57j-28jc-8429 + * for an example of what can go wrong. + * + * @deprecated This will not be used by any future version without any further notice. + */ +abstract class SearchColumnsProviderHook +{ + abstract public function retrieveCustomVars(Model $model): array; + + final public static function getCustomVarColumns(Model $model, array $defaultColumns = []): array + { + $columns = $defaultColumns; + + $hooks = Hook::all('icingadb/SearchColumnsProvider'); + foreach ($hooks as $hook) { + try { + $customVars = $hook->retrieveCustomVars($model); + if (! empty($customVars)) { + $columns = [...$columns, ...$customVars]; + } + } catch (Throwable $e) { + Logger::error( + 'Error retrieving Custom Vars for %s with table name "%s": %s', + get_class($model), + $model->getTableName(), + $e->getMessage() + ); + } + } + + return array_unique($columns); + } +} diff --git a/library/Icingadb/Web/Control/SearchBar/ObjectSuggestions.php b/library/Icingadb/Web/Control/SearchBar/ObjectSuggestions.php index ccc87e028..1856b489e 100644 --- a/library/Icingadb/Web/Control/SearchBar/ObjectSuggestions.php +++ b/library/Icingadb/Web/Control/SearchBar/ObjectSuggestions.php @@ -9,6 +9,7 @@ use Icinga\Module\Icingadb\Common\Database; use Icinga\Module\Icingadb\Data\QueryColumnsProvider; use Icinga\Module\Icingadb\Data\QueryValuesProvider; +use Icinga\Module\Icingadb\Hook\SearchColumnsProviderHook; use ipl\Html\HtmlElement; use ipl\Orm\Model; use ipl\Stdlib\BaseFilter; @@ -92,7 +93,10 @@ protected function createQuickSearchFilter($searchTerm) $resolver = $model::on($this->getDb())->getResolver(); $quickFilter = Filter::any(); - foreach ($model->getSearchColumns() as $column) { + $defaultColumns = $model->getSearchColumns(); + $columns = SearchColumnsProviderHook::getCustomVarColumns($model, $defaultColumns); + + foreach ($columns as $column) { if (strpos($column, '.') === false) { $column = $resolver->qualifyColumn($column, $model->getTableName()); } diff --git a/library/Icingadb/Web/Controller.php b/library/Icingadb/Web/Controller.php index ede186c01..1d07bfe9b 100644 --- a/library/Icingadb/Web/Controller.php +++ b/library/Icingadb/Web/Controller.php @@ -13,7 +13,6 @@ use Icinga\Application\Logger; use Icinga\Application\Web; use Icinga\Data\ConfigObject; -use Icinga\Exception\Http\HttpBadRequestException; use Icinga\Exception\Json\JsonDecodeException; use Icinga\Module\Icingadb\Common\Auth; use Icinga\Module\Icingadb\Common\Database; @@ -23,6 +22,7 @@ use Icinga\Module\Icingadb\Data\JsonResultSet; use Icinga\Module\Icingadb\Data\QueryColumnsProvider; use Icinga\Module\Icingadb\Web\Control\ColumnChooser; +use Icinga\Module\Icingadb\Hook\SearchColumnsProviderHook; use Icinga\Module\Icingadb\Web\Control\GridViewModeSwitcher; use Icinga\Module\Icingadb\Web\Control\TimestampToggle; use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher; @@ -349,7 +349,9 @@ public function handleSearchRequest(Query $query, array $additionalColumns = []) */ protected function prepareSearchFilter(Query $query, string $search, Filter\Any $filter, array $additionalColumns) { - $columns = array_merge($query->getModel()->getSearchColumns(), $additionalColumns); + $model = $query->getModel(); + $defaultColumns = $model->getSearchColumns(); + $columns = SearchColumnsProviderHook::getCustomVarColumns($model, $defaultColumns); foreach ($columns as $column) { if (strpos($column, '.') === false) { $column = $query->getResolver()->qualifyColumn($column, $query->getModel()->getTableName());