diff --git a/application/clicommands/MigrateCommand.php b/application/clicommands/MigrateCommand.php new file mode 100644 index 000000000..6d034ee39 --- /dev/null +++ b/application/clicommands/MigrateCommand.php @@ -0,0 +1,835 @@ +setLevel(Logger::INFO); + } + + /** + * Migrate monitoring navigation items to Icinga DB Web + * + * USAGE + * + * icingacli icingadb migrate navigation [options] + * + * REQUIRED OPTIONS: + * + * --user= Migrate navigation items whose owner matches the given + * name or owners matching the given pattern. Wildcard + * matching by `*` possible. + * + * OPTIONS: + * + * --override Replace existing or already migrated items + * (Attention: Actions are not backed up) + * + * --no-backup Remove monitoring actions and don't back up menu items + */ + public function navigationAction(): void + { + /** @var string $user */ + $user = $this->params->getRequired('user'); + $noBackup = $this->params->get('no-backup'); + + $preferencesPath = Config::resolvePath('preferences'); + $sharedNavigation = Config::resolvePath('navigation'); + if (! file_exists($preferencesPath) && ! file_exists($sharedNavigation)) { + Logger::info('There are no user navigation items to migrate'); + return; + } + + $rc = 0; + $directories = file_exists($preferencesPath) ? new DirectoryIterator($preferencesPath) : []; + + $anythingChanged = false; + + /** @var string $directory */ + foreach ($directories as $directory) { + /** @var string $username */ + $username = $directories->key() === false ? '' : $directories->key(); + if (fnmatch($user, $username) === false) { + continue; + } + + $menuItems = $this->readFromIni($directory . '/menu.ini', $rc); + $hostActions = $this->readFromIni($directory . '/host-actions.ini', $rc); + $serviceActions = $this->readFromIni($directory . '/service-actions.ini', $rc); + $icingadbHostActions = $this->readFromIni($directory . '/icingadb-host-actions.ini', $rc); + $icingadbServiceActions = $this->readFromIni($directory . '/icingadb-service-actions.ini', $rc); + + $menuUpdated = false; + $originalMenuItems = $this->readFromIni($directory . '/menu.ini', $rc); + + Logger::info( + 'Transforming legacy wildcard filters of existing Icinga DB Web items for user "%s"', + $username + ); + + if (! $menuItems->isEmpty()) { + $menuUpdated = $this->transformNavigationItems($menuItems, $username, $rc); + $anythingChanged |= $menuUpdated; + } + + if (! $icingadbHostActions->isEmpty()) { + $anythingChanged |= $this->transformNavigationItems($icingadbHostActions, $username, $rc); + } + + if (! $icingadbServiceActions->isEmpty()) { + $anythingChanged |= $this->transformNavigationItems( + $icingadbServiceActions, + $username, + $rc + ); + } + + if (! $this->skipMigration) { + Logger::info('Migrating monitoring navigation items for user "%s" to Icinga DB Web', $username); + + if (! $menuItems->isEmpty()) { + $menuUpdated = $this->migrateNavigationItems($menuItems, $username, $directory . '/menu.ini', $rc); + $anythingChanged |= $menuUpdated; + } + + if (! $hostActions->isEmpty()) { + $anythingChanged |= $this->migrateNavigationItems( + $hostActions, + $username, + $directory . '/icingadb-host-actions.ini', + $rc + ); + } + + if (! $serviceActions->isEmpty()) { + $anythingChanged |= $this->migrateNavigationItems( + $serviceActions, + $username, + $directory . '/icingadb-service-actions.ini', + $rc + ); + } + } + + if ($menuUpdated && ! $noBackup) { + $this->createBackupIni("$directory/menu", $originalMenuItems); + } + } + + // Start migrating shared navigation items + $menuItems = $this->readFromIni($sharedNavigation . '/menu.ini', $rc); + $hostActions = $this->readFromIni($sharedNavigation . '/host-actions.ini', $rc); + $serviceActions = $this->readFromIni($sharedNavigation . '/service-actions.ini', $rc); + $icingadbHostActions = $this->readFromIni($sharedNavigation . '/icingadb-host-actions.ini', $rc); + $icingadbServiceActions = $this->readFromIni($sharedNavigation . '/icingadb-service-actions.ini', $rc); + + $menuUpdated = false; + $originalMenuItems = $this->readFromIni($sharedNavigation . '/menu.ini', $rc); + + Logger::info('Transforming legacy wildcard filters of existing shared Icinga DB Web navigation items'); + + if (! $menuItems->isEmpty()) { + $menuUpdated = $this->transformNavigationItems($menuItems, $user, $rc); + $anythingChanged |= $menuUpdated; + } + + if (! $icingadbHostActions->isEmpty()) { + $anythingChanged |= $this->transformNavigationItems($icingadbHostActions, $user, $rc); + } + + if (! $icingadbServiceActions->isEmpty()) { + $anythingChanged |= $this->transformNavigationItems( + $icingadbServiceActions, + $user, + $rc + ); + } + + if (! $this->skipMigration) { + Logger::info('Migrating shared monitoring navigation items to the Icinga DB Web items'); + + if (! $menuItems->isEmpty()) { + $menuUpdated = $this->migrateNavigationItems($menuItems, $user, $sharedNavigation . '/menu.ini', $rc); + $anythingChanged |= $menuUpdated; + } + + if (! $hostActions->isEmpty()) { + $anythingChanged |= $this->migrateNavigationItems( + $hostActions, + $user, + $sharedNavigation . '/icingadb-host-actions.ini', + $rc + ); + } + + if (! $serviceActions->isEmpty()) { + $anythingChanged |= $this->migrateNavigationItems( + $serviceActions, + $user, + $sharedNavigation . '/icingadb-service-actions.ini', + $rc + ); + } + } + + if ($menuUpdated && ! $noBackup) { + $this->createBackupIni("$sharedNavigation/menu", $originalMenuItems); + } + + if ($rc > 0) { + if ($this->skipMigration) { + Logger::error('Failed to transform some icingadb navigation items'); + } else { + Logger::error('Failed to migrate some monitoring navigation items'); + } + + exit($rc); + } + + if (! $anythingChanged) { + Logger::info('Nothing to do'); + } elseif ($this->skipMigration) { + Logger::info('Successfully transformed all icingadb navigation item filters'); + } else { + Logger::info('Successfully migrated all monitoring navigation items'); + } + } + + + /** + * Migrate monitoring restrictions and permissions to Icinga DB Web + * + * Migrated roles do not grant general or full access to users afterward. + * It is recommended to review any changes made by this command, before + * manually granting access. + * + * USAGE + * + * icingacli icingadb migrate role [options] + * + * REQUIRED OPTIONS: (Use either, not both) + * + * --group= Update roles that are assigned to the given group or to + * groups matching the pattern. Wildcard matching by `*` + * possible. + * + * --role= Update role with the given name or roles whose names + * match the pattern. Wildcard matching by `*` possible. + * + * OPTIONS: + * + * --override Reset any existing Icinga DB Web rules + * + * --no-backup Don't back up roles + */ + public function roleAction(): void + { + /** @var ?bool $override */ + $override = $this->params->get('override'); + $noBackup = $this->params->get('no-backup'); + + /** @var ?string $groupName */ + $groupName = $this->params->get('group'); + /** @var ?string $roleName */ + $roleName = $this->params->get('role'); + + if ($roleName === null && $groupName === null) { + $this->fail("One of the parameters 'group' or 'role' must be supplied"); + } elseif ($roleName !== null && $groupName !== null) { + $this->fail("Use either 'group' or 'role'. Both cannot be used as role overrules group."); + } + + $rc = 0; + $changed = false; + + $restrictions = Config::$configDir . '/roles.ini'; + $rolesConfig = $this->readFromIni($restrictions, $rc); + $monitoringRestriction = 'monitoring/filter/objects'; + $monitoringPropertyBlackList = 'monitoring/blacklist/properties'; + $icingadbRestrictions = [ + 'objects' => 'icingadb/filter/objects', + 'hosts' => 'icingadb/filter/hosts', + 'services' => 'icingadb/filter/services' + ]; + + $icingadbPropertyDenyList = 'icingadb/denylist/variables'; + foreach ($rolesConfig as $name => $role) { + /** @var string[] $role */ + $role = iterator_to_array($role); + + if ($roleName === '*' || $groupName === '*') { + $roleMatch = true; + } elseif ($roleName !== null && fnmatch($roleName, $name)) { + $roleMatch = true; + } elseif ($groupName !== null && isset($role['groups'])) { + $roleGroups = array_map('trim', explode(',', $role['groups'])); + $roleMatch = false; + foreach ($roleGroups as $roleGroup) { + if (fnmatch($groupName, $roleGroup)) { + $roleMatch = true; + break; + } + } + } else { + $roleMatch = false; + } + + if ($roleMatch && ! $this->skipMigration && $this->shouldUpdateRole($role, $override)) { + if (isset($role[$monitoringRestriction])) { + Logger::info( + 'Migrating monitoring restriction filter for role "%s" to the Icinga DB Web restrictions', + $name + ); + $transformedFilter = UrlMigrator::transformFilter( + QueryString::parse($role[$monitoringRestriction]) + ); + + if ($transformedFilter) { + $role[$icingadbRestrictions['objects']] = QueryString::render($transformedFilter); + $changed = true; + } + } + + if (isset($role[$monitoringPropertyBlackList])) { + Logger::info( + 'Migrating monitoring blacklisted properties for role "%s" to the Icinga DB Web deny list', + $name + ); + + $icingadbProperties = []; + foreach (explode(',', $role[$monitoringPropertyBlackList]) as $property) { + $icingadbProperties[] = preg_replace('/^(?:host|service)\.vars\./i', '', $property, 1); + } + + $role[$icingadbPropertyDenyList] = str_replace( + '**', + '*', + implode(',', array_unique($icingadbProperties)) + ); + + $changed = true; + } + + if (isset($role['permissions'])) { + $updatedPermissions = []; + Logger::info( + 'Migrating monitoring permissions for role "%s" to the Icinga DB Web permissions', + $name + ); + + if (strpos($role['permissions'], 'monitoring')) { + $monitoringProtection = Config::module('monitoring') + ->get('security', 'protected_customvars'); + + if ($monitoringProtection !== null) { + $role['icingadb/protect/variables'] = $monitoringProtection; + $changed = true; + } + } + + foreach (explode(',', $role['permissions']) as $permission) { + if (Str::startsWith($permission, 'icingadb/') || $permission === 'module/icingadb') { + continue; + } elseif (Str::startsWith($permission, 'monitoring/command/')) { + $changed = true; + $updatedPermissions[] = $permission; + $updatedPermissions[] = str_replace('monitoring/', 'icingadb/', $permission); + } elseif ($permission === 'no-monitoring/contacts') { + $changed = true; + $updatedPermissions[] = $permission; + $role['icingadb/denylist/routes'] = 'users,usergroups'; + } else { + $updatedPermissions[] = $permission; + } + } + + $role['permissions'] = implode(',', $updatedPermissions); + } + + if (isset($role['refusals']) && is_string($role['refusals'])) { + $updatedRefusals = []; + Logger::info( + 'Migrating monitoring refusals for role "%s" to the Icinga DB Web refusals', + $name + ); + + foreach (explode(',', $role['refusals']) as $refusal) { + if (Str::startsWith($refusal, 'icingadb/') || $refusal === 'module/icingadb') { + continue; + } elseif (Str::startsWith($refusal, 'monitoring/command/')) { + $changed = true; + $updatedRefusals[] = $refusal; + $updatedRefusals[] = str_replace('monitoring/', 'icingadb/', $refusal); + } else { + $updatedRefusals[] = $refusal; + } + } + + $role['refusals'] = implode(',', $updatedRefusals); + } + } + + if ($roleMatch) { + foreach ($icingadbRestrictions as $object => $icingadbRestriction) { + if (isset($role[$icingadbRestriction]) && is_string($role[$icingadbRestriction])) { + $filter = QueryString::parse($role[$icingadbRestriction]); + $filter = UrlMigrator::transformLegacyWildcardFilter($filter); + + if ($filter) { + $filter = QueryString::render($filter); + if ($filter !== $role[$icingadbRestriction]) { + Logger::info( + 'Icinga Db Web restriction of role "%s" for %s changed from "%s" to "%s"', + $name, + $object, + $role[$icingadbRestriction], + $filter + ); + + $role[$icingadbRestriction] = $filter; + $changed = true; + } + } + } + } + } + + $rolesConfig->setSection($name, $role); + } + + if ($changed) { + if (! $noBackup) { + $this->createBackupIni(Config::$configDir . '/roles'); + } + + try { + $rolesConfig->saveIni(); + } catch (NotWritableError $error) { + Logger::error($error); + if ($this->skipMigration) { + Logger::error('Failed to transform icingadb restrictions'); + } else { + Logger::error('Failed to migrate monitoring restrictions'); + } + + exit(256); + } + + if ($this->skipMigration) { + Logger::info('Successfully transformed all icingadb restrictions'); + } else { + Logger::info('Successfully migrated monitoring restrictions and permissions in roles'); + } + } else { + Logger::info('Nothing to do'); + } + } + + /** + * Migrate monitoring dashboards to Icinga DB Web + * + * USAGE + * + * icingacli icingadb migrate dashboard [options] + * + * REQUIRED OPTIONS: + * + * --user= Migrate dashboards whose owner matches the given + * name or owners matching the given pattern. Wildcard + * matching by `*` possible. + * + * OPTIONS: + * + * --no-backup Don't back up dashboards + */ + public function dashboardAction(): void + { + /** @var string $user */ + $user = $this->params->getRequired('user'); + $noBackup = $this->params->get('no-backup'); + + $dashboardsPath = Config::resolvePath('dashboards'); + if (! file_exists($dashboardsPath)) { + Logger::info('There are no dashboards to migrate'); + return; + } + + $rc = 0; + $directories = new DirectoryIterator($dashboardsPath); + + $anythingChanged = false; + + /** @var string $directory */ + foreach ($directories as $directory) { + /** @var string $userName */ + $userName = $directories->key() === false ? '' : $directories->key(); + if (fnmatch($user, $userName) === false) { + continue; + } + + $dashboardsConfig = $this->readFromIni($directory . '/dashboard.ini', $rc); + $backupConfig = $this->readFromIni($directory . '/dashboard.ini', $rc); + + Logger::info( + 'Migrating monitoring dashboards to Icinga DB Web dashboards for user "%s"', + $userName + ); + + $changed = false; + /** @var ConfigObject $dashboardConfig */ + foreach ($dashboardsConfig->getConfigObject() as $name => $dashboardConfig) { + /** @var ?string $dashboardUrlString */ + $dashboardUrlString = $dashboardConfig->get('url'); + if ($dashboardUrlString !== null) { + $dashBoardUrl = Url::fromPath($dashboardUrlString, [], new Request()); + if (! $this->skipMigration && Str::startsWith(ltrim($dashboardUrlString, '/'), 'monitoring/')) { + $dashboardConfig->url = UrlMigrator::transformUrl($dashBoardUrl)->getRelativeUrl(); + $changed = true; + } + + if (Str::startsWith(ltrim($dashboardUrlString, '/'), 'icingadb/')) { + $finalUrl = $dashBoardUrl->onlyWith(['sort', 'limit', 'view', 'columns', 'page']); + $params = $dashBoardUrl->without(['sort', 'limit', 'view', 'columns', 'page'])->getParams(); + $filter = QueryString::parse($params->toString()); + $filter = UrlMigrator::transformLegacyWildcardFilter($filter); + if ($filter) { + $oldFilterString = $params->toString(); + $newFilterString = QueryString::render($filter); + + if ($oldFilterString !== $newFilterString) { + Logger::info( + 'Icinga Db Web filter of dashboard "%s" has changed from "%s" to "%s"', + $name, + $params->toString(), + QueryString::render($filter) + ); + $finalUrl->setFilter($filter); + + $dashboardConfig->url = $finalUrl->getRelativeUrl(); + $changed = true; + } + } + } + } + } + + + if ($changed && $noBackup === null) { + $this->createBackupIni("$directory/dashboard", $backupConfig); + } + + if ($changed) { + $anythingChanged = true; + } + + try { + $dashboardsConfig->saveIni(); + } catch (NotWritableError $error) { + Logger::error($error); + $rc = 256; + } + } + + if ($rc > 0) { + if ($this->skipMigration) { + Logger::error('Failed to transform some icingadb dashboards'); + } else { + Logger::error('Failed to migrate some monitoring dashboards'); + } + + exit($rc); + } + + if (! $anythingChanged) { + Logger::info('Nothing to do'); + } elseif ($this->skipMigration) { + Logger::info('Successfully transformed all icingadb dashboards'); + } else { + Logger::info('Successfully migrated dashboards for all the matched users'); + } + } + + /** + * Migrate Icinga DB Web wildcard filters of navigation items, dashboards and roles + * + * USAGE + * + * icingacli icingadb migrate filter + * + * OPTIONS: + * + * --no-backup Don't back up menu items, dashboards and roles + */ + public function filterAction(): void + { + $this->skipMigration = true; + + $this->params->set('user', '*'); + $this->navigationAction(); + $this->dashboardAction(); + + $this->params->set('role', '*'); + $this->roleAction(); + } + + private function transformNavigationItems(Config $config, string $owner, int &$rc): bool + { + $updated = false; + /** @var ConfigObject $newConfigObject */ + foreach ($config->getConfigObject() as $section => $newConfigObject) { + /** @var string $configOwner */ + $configOwner = $newConfigObject->get('owner') ?? ''; + if ($configOwner && $configOwner !== $owner) { + continue; + } + + if ( + $newConfigObject->get('type') === 'icingadb-host-action' + || $newConfigObject->get('type') === 'icingadb-service-action' + ) { + /** @var ?string $legacyFilter */ + $legacyFilter = $newConfigObject->get('filter'); + if ($legacyFilter !== null) { + $filter = QueryString::parse($legacyFilter); + $filter = UrlMigrator::transformLegacyWildcardFilter($filter); + if ($filter) { + $filter = QueryString::render($filter); + if ($legacyFilter !== $filter) { + $newConfigObject->filter = $filter; + $updated = true; + Logger::info( + 'Icinga DB Web filter of action "%s" is changed from %s to "%s"', + $section, + $legacyFilter, + $filter + ); + } + } + } + } + + /** @var string $url */ + $url = $newConfigObject->get('url'); + if ($url && Str::startsWith(ltrim($url, '/'), 'icingadb/')) { + $url = Url::fromPath($url, [], new Request()); + $finalUrl = $url->onlyWith(['sort', 'limit', 'view', 'columns', 'page']); + $params = $url->without(['sort', 'limit', 'view', 'columns', 'page'])->getParams(); + $filter = QueryString::parse($params->toString()); + $filter = UrlMigrator::transformLegacyWildcardFilter($filter); + if ($filter) { + $oldFilterString = $params->toString(); + $newFilterString = QueryString::render($filter); + + if ($oldFilterString !== $newFilterString) { + Logger::info( + 'Icinga Db Web filter of navigation item "%s" has changed from "%s" to "%s"', + $section, + $oldFilterString, + $newFilterString + ); + + $newConfigObject->url = $finalUrl->setFilter($filter)->getRelativeUrl(); + $updated = true; + } + } + } + } + + if ($updated) { + try { + $config->saveIni(); + } catch (NotWritableError $error) { + Logger::error($error); + $rc = 256; + + return false; + } + } + + return $updated; + } + + /** + * Migrate the given config to the given new config path + * + * @param Config $config + * @param string $owner + * @param string $path + * @param int $rc + * + * @return bool + */ + private function migrateNavigationItems(Config $config, string $owner, string $path, int &$rc): bool + { + $deleteLegacyFiles = $this->params->get('no-backup'); + $override = $this->params->get('override'); + $newConfig = $config->getConfigFile() === $path ? $config : $this->readFromIni($path, $rc); + + $updated = false; + /** @var ConfigObject $configObject */ + foreach ($config->getConfigObject() as $configObject) { + /** @var string $configOwner */ + $configOwner = $configObject->get('owner') ?? ''; + if ($configOwner && $configOwner !== $owner) { + continue; + } + + $migrateFilter = false; + if ($configObject->type === 'host-action') { + $updated = true; + $migrateFilter = true; + $configObject->type = 'icingadb-host-action'; + } elseif ($configObject->type === 'service-action') { + $updated = true; + $migrateFilter = true; + $configObject->type = 'icingadb-service-action'; + } + + /** @var ?string $urlString */ + $urlString = $configObject->get('url'); + if ($urlString !== null) { + $urlString = str_replace( + ['$SERVICEDESC$', '$HOSTNAME$', '$HOSTADDRESS$', '$HOSTADDRESS6$'], + ['$service.name$', '$host.name$', '$host.address$', '$host.address6$'], + $urlString + ); + if ($urlString !== $configObject->url) { + $configObject->url = $urlString; + $updated = true; + } + + $url = Url::fromPath($urlString, [], new Request()); + + try { + $urlString = UrlMigrator::transformUrl($url)->getRelativeUrl(); + $configObject->url = $urlString; + $updated = true; + } catch (\InvalidArgumentException $err) { + // Do nothing + } + } + + /** @var ?string $legacyFilter */ + $legacyFilter = $configObject->get('filter'); + if ($migrateFilter && $legacyFilter) { + $updated = true; + $filter = QueryString::parse($legacyFilter); + $filter = UrlMigrator::transformFilter($filter); + if ($filter !== false) { + $configObject->filter = QueryString::render($filter); + } else { + unset($configObject->filter); + } + } + + $section = $config->key(); + if (! $newConfig->hasSection($section) || $newConfig === $config || $override) { + $newConfig->setSection($section, $configObject); + } + } + + if ($updated) { + try { + $newConfig->saveIni(); + + // Remove the legacy file only if explicitly requested + if ($deleteLegacyFiles && $newConfig !== $config) { + unlink($config->getConfigFile()); + } + } catch (NotWritableError $error) { + Logger::error($error); + $rc = 256; + + return false; + } + } + + return $updated; + } + + /** + * Get the navigation items config from the given ini path + * + * @param string $path Absolute path of the ini file + * @param int $rc The return code used to exit the action + * + * @return Config + */ + private function readFromIni($path, &$rc) + { + try { + $config = Config::fromIni($path); + } catch (NotReadableError $error) { + Logger::error($error); + + $config = new Config(); + $rc = 128; + } + + return $config; + } + + private function createBackupIni(string $path, Config $config = null): void + { + $counter = 0; + while (true) { + $filepath = $counter > 0 + ? "$path.backup$counter.ini" + : "$path.backup.ini"; + + if (! file_exists($filepath)) { + if ($config) { + $config->saveIni($filepath); + } else { + copy("$path.ini", $filepath); + } + + break; + } else { + $counter++; + } + } + } + + /** + * Checks if the given role should be updated + * + * @param string[] $role + * @param bool $override + * + * @return bool + */ + private function shouldUpdateRole(array $role, ?bool $override): bool + { + return ! ( + isset($role['icingadb/filter/objects']) + || isset($role['icingadb/filter/hosts']) + || isset($role['icingadb/filter/services']) + || isset($role['icingadb/denylist/routes']) + || isset($role['icingadb/denylist/variables']) + || isset($role['icingadb/protect/variables']) + || (isset($role['permissions']) && str_contains($role['permissions'], 'icingadb')) + ) + || $override; + } +} diff --git a/test/php/application/clicommands/MigrateCommandTest.php b/test/php/application/clicommands/MigrateCommandTest.php new file mode 100644 index 000000000..2f591acac --- /dev/null +++ b/test/php/application/clicommands/MigrateCommandTest.php @@ -0,0 +1,1727 @@ + [ + 'initial' => [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'hosts.group_members' => [ + 'title' => 'Group Members', + 'url' => 'monitoring/list/hosts?hostgroup_name=group1|hostgroup_name=%28group2%29' + ], + 'hosts.variables' => [ + 'title' => 'Host Variables', + 'url' => 'monitoring/list/hosts?(_host_foo=bar&_host_bar=foo)|_host_rab=oof' + ], + 'hosts.wildcards' => [ + 'title' => 'Host Wildcards', + 'url' => 'monitoring/list/hosts?host_name=%2Afoo%2A|host_name=%2Abar%2A' + . '&sort=host_severity&dir=asc&limit=25' + ], + 'hosts.encoded_params' => [ + 'title' => 'Host Encoded Params', + 'url' => 'monitoring/list/hosts?host_name=%28foo%29&sort=_host_%28foo%29' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.no-wildcards' => [ + 'title' => 'No Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name=linux-hosts' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name=%2Alinux%2A' + ], + 'icingadb.also-wildcards' => [ + 'title' => 'Also Wildcards', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A' + ], + 'icingadb.with-sort-and-limit' => [ + 'title' => 'With Sort And Limit', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A|host.name=bar&sort=host.state.severity&limit=50' + ], + 'not-monitoring-or-icingadb' => [ + 'title' => 'Not Monitoring Or Icinga DB' + ], + 'not-monitoring-or-icingadb.something' => [ + 'title' => 'Something', + 'url' => 'somewhere/something?foo=%2Abar%2A' + ] + ], + 'expected' => [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'hosts.group_members' => [ + 'title' => 'Group Members', + 'url' => 'icingadb/hosts?hostgroup.name=group1|hostgroup.name=%28group2%29' + ], + 'hosts.variables' => [ + 'title' => 'Host Variables', + 'url' => 'icingadb/hosts?(host.vars.foo=bar&host.vars.bar=foo)|host.vars.rab=oof' + ], + 'hosts.wildcards' => [ + 'title' => 'Host Wildcards', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A|host.name~%2Abar%2A' + . '&sort=host.state.severity%20asc&limit=25' + ], + 'hosts.encoded_params' => [ + 'title' => 'Host Encoded Params', + 'url' => 'icingadb/hosts?host.name=%28foo%29&sort=host.vars.%28foo%29' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.no-wildcards' => [ + 'title' => 'No Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name=linux-hosts' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name~%2Alinux%2A' + ], + 'icingadb.also-wildcards' => [ + 'title' => 'Also Wildcards', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A' + ], + 'icingadb.with-sort-and-limit' => [ + 'title' => 'With Sort And Limit', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A|host.name=bar&sort=host.state.severity&limit=50' + ], + 'not-monitoring-or-icingadb' => [ + 'title' => 'Not Monitoring Or Icinga DB' + ], + 'not-monitoring-or-icingadb.something' => [ + 'title' => 'Something', + 'url' => 'somewhere/something?foo=%2Abar%2A' + ] + ] + ], + 'menu-items' => [ + 'initial' => [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A' + ] + ], + 'expected' => [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A' + ] + ] + ], + 'shared-menu-items' => [ + 'initial' => [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo', + 'owner' => 'test' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1', + 'owner' => 'test' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A', + 'owner' => 'test' + ], + 'other-monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1', + 'owner' => 'not-test' + ] + ], + 'expected' => [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo', + 'owner' => 'test' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.state.is_problem=y', + 'owner' => 'test' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A', + 'owner' => 'test' + ], + 'other-monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1', + 'owner' => 'not-test' + ] + ] + ], + 'host-actions' => [ + 'initial' => [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A' + ], + 'hosts_encoded_params' => [ + 'type' => 'host-action', + 'url' => 'monitoring/list/hosts?host_name=%28foo%29&sort=_host_%28foo%29', + 'filter' => '_host_%28foo%29=bar' + ] + ], + 'expected' => [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A' + ], + 'hosts_encoded_params' => [ + 'type' => 'icingadb-host-action', + 'url' => 'icingadb/hosts?host.name=%28foo%29&sort=host.vars.%28foo%29', + 'filter' => 'host.vars.%28foo%29=bar' + ] + ] + ], + 'icingadb-host-actions' => [ + 'initial' => [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name=%2Afoo%2A' + ] + ], + 'expected' => [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A' + ] + ] + ], + 'service-actions' => [ + 'initial' => [ + 'services' => [ + 'type' => 'service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => '_service_foo=bar&_service_bar=%2Afoo%2A' + ], + 'services_encoded_params' => [ + 'type' => 'host-action', + 'url' => 'monitoring/list/services?host_name=%28foo%29&sort=_host_%28foo%29', + 'filter' => '_host_%28foo%29=bar' + ] + ], + 'expected' => [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar~%2Afoo%2A' + ], + 'services_encoded_params' => [ + 'type' => 'icingadb-host-action', + 'url' => 'icingadb/services?host.name=%28foo%29&sort=host.vars.%28foo%29', + 'filter' => 'host.vars.%28foo%29=bar' + ] + ] + ], + 'icingadb-service-actions' => [ + 'initial' => [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=%2Abar%2A' + ] + ], + 'expected' => [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo~%2Abar%2A' + ] + ] + ], + 'shared-host-actions' => [ + 'initial' => [ + 'shared-hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A', + 'owner' => 'test' + ], + 'hosts_encoded_params' => [ + 'type' => 'host-action', + 'url' => 'monitoring/list/hosts?host_name=%28foo%29&sort=_host_%28foo%29', + 'filter' => '_host_%28foo%29=bar', + 'owner' => 'test' + ], + 'other-hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A', + 'owner' => 'not-test' + ] + ], + 'expected' => [ + 'shared-hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A', + 'owner' => 'test' + ], + 'hosts_encoded_params' => [ + 'type' => 'icingadb-host-action', + 'url' => 'icingadb/hosts?host.name=%28foo%29&sort=host.vars.%28foo%29', + 'filter' => 'host.vars.%28foo%29=bar', + 'owner' => 'test' + ] + ] + ], + 'host-actions-legacy-macros' => [ + 'initial' => [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$HOSTNAME$,$HOSTADDRESS$,$HOSTADDRESS6$', + 'filter' => 'host_name=%2Afoo%2A' + ] + ], + 'expected' => [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$,$host.address$,$host.address6$', + 'filter' => 'host.name~%2Afoo%2A' + ] + ] + ], + 'service-actions-legacy-macros' => [ + 'initial' => [ + 'services' => [ + 'type' => 'service-action', + 'url' => 'example.com/search?q=$SERVICEDESC$,$HOSTNAME$,$HOSTADDRESS$,$HOSTADDRESS6$', + 'filter' => '_service_foo=bar&_service_bar=%2Afoo%2A' + ] + ], + 'expected' => [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar~%2Afoo%2A' + ] + ] + ], + 'all-roles' => [ + 'initial' => [ + 'no-wildcards' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'wildcards' => [ + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A|hostgroup_name=%2Afoo%2A' + ], + 'encoded_column' => [ + 'monitoring/filter/objects' => '_host_%28foo%29=bar' + ], + 'blacklist' => [ + 'monitoring/blacklist/properties' => 'host.vars.foo,service.vars.bar*,host.vars.a.**.d' + ], + 'full-access' => [ + 'permissions' => 'module/monitoring,monitoring/*' + ], + 'general-read-access' => [ + 'permissions' => 'module/monitoring' + ], + 'general-write-access' => [ + 'permissions' => 'module/monitoring,monitoring/command/*' + ], + 'full-fine-grained-access' => [ + 'permissions' => 'module/monitoring' + . ',monitoring/command/schedule-check' + . ',monitoring/command/acknowledge-problem' + . ',monitoring/command/remove-acknowledgement' + . ',monitoring/command/comment/add' + . ',monitoring/command/comment/delete' + . ',monitoring/command/downtime/schedule' + . ',monitoring/command/downtime/delete' + . ',monitoring/command/process-check-result' + . ',monitoring/command/feature/instance' + . ',monitoring/command/feature/object/active-checks' + . ',monitoring/command/feature/object/passive-checks' + . ',monitoring/command/feature/object/notifications' + . ',monitoring/command/feature/object/event-handler' + . ',monitoring/command/feature/object/flap-detection' + . ',monitoring/command/send-custom-notification' + ], + 'full-with-refusals' => [ + 'permissions' => 'module/monitoring,monitoring/command/*', + 'refusals' => 'monitoring/command/downtime/*,monitoring/command/feature/instance' + ], + 'active-only' => [ + 'permissions' => 'module/monitoring,monitoring/command/schedule-check/active-only' + ], + 'no-monitoring-contacts' => [ + 'permissions' => 'module/monitoring,no-monitoring/contacts' + ], + 'reporting-only' => [ + 'permissions' => 'module/reporting' + ], + 'icingadb' => [ + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A|hostgroup.name=%2Afoo%2A', + 'icingadb/filter/services' => 'service.name=%2Abar%2A&service.vars.env=prod', + 'icingadb/filter/hosts' => 'host.vars.env=%2Afoo%2A' + ] + ], + 'expected' => [ + 'no-wildcards' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo', + 'icingadb/filter/objects' => 'host.name=foo|hostgroup.name=foo' + ], + 'wildcards' => [ + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A|hostgroup_name=%2Afoo%2A', + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A|hostgroup.name~%2Afoo%2A' + ], + 'encoded_column' => [ + 'monitoring/filter/objects' => '_host_%28foo%29=bar', + 'icingadb/filter/objects' => 'host.vars.%28foo%29=bar' + ], + 'blacklist' => [ + 'monitoring/blacklist/properties' => 'host.vars.foo,service.vars.bar*,host.vars.a.**.d', + 'icingadb/denylist/variables' => 'foo,bar*,a.*.d' + ], + 'full-access' => [ + 'permissions' => 'module/monitoring,monitoring/*' + ], + 'general-read-access' => [ + 'permissions' => 'module/monitoring' + ], + 'general-write-access' => [ + 'permissions' => 'module/monitoring,monitoring/command/*,icingadb/command/*' + ], + 'full-fine-grained-access' => [ + 'permissions' => 'module/monitoring' + . ',monitoring/command/schedule-check' + . ',icingadb/command/schedule-check' + . ',monitoring/command/acknowledge-problem' + . ',icingadb/command/acknowledge-problem' + . ',monitoring/command/remove-acknowledgement' + . ',icingadb/command/remove-acknowledgement' + . ',monitoring/command/comment/add' + . ',icingadb/command/comment/add' + . ',monitoring/command/comment/delete' + . ',icingadb/command/comment/delete' + . ',monitoring/command/downtime/schedule' + . ',icingadb/command/downtime/schedule' + . ',monitoring/command/downtime/delete' + . ',icingadb/command/downtime/delete' + . ',monitoring/command/process-check-result' + . ',icingadb/command/process-check-result' + . ',monitoring/command/feature/instance' + . ',icingadb/command/feature/instance' + . ',monitoring/command/feature/object/active-checks' + . ',icingadb/command/feature/object/active-checks' + . ',monitoring/command/feature/object/passive-checks' + . ',icingadb/command/feature/object/passive-checks' + . ',monitoring/command/feature/object/notifications' + . ',icingadb/command/feature/object/notifications' + . ',monitoring/command/feature/object/event-handler' + . ',icingadb/command/feature/object/event-handler' + . ',monitoring/command/feature/object/flap-detection' + . ',icingadb/command/feature/object/flap-detection' + . ',monitoring/command/send-custom-notification' + . ',icingadb/command/send-custom-notification' + ], + 'full-with-refusals' => [ + 'permissions' => 'module/monitoring,monitoring/command/*,icingadb/command/*', + 'refusals' => 'monitoring/command/downtime/*' + . ',icingadb/command/downtime/*' + . ',monitoring/command/feature/instance' + . ',icingadb/command/feature/instance' + ], + 'active-only' => [ + 'permissions' => 'module/monitoring' + . ',monitoring/command/schedule-check/active-only' + . ',icingadb/command/schedule-check/active-only' + ], + 'no-monitoring-contacts' => [ + 'permissions' => 'module/monitoring,no-monitoring/contacts', + 'icingadb/denylist/routes' => 'users,usergroups' + ], + 'reporting-only' => [ + 'permissions' => 'module/reporting' + ], + 'icingadb' => [ + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A|hostgroup.name~%2Afoo%2A', + 'icingadb/filter/services' => 'service.name~%2Abar%2A&service.vars.env=prod', + 'icingadb/filter/hosts' => 'host.vars.env~%2Afoo%2A' + ] + ] + ], + 'single-role-or-group' => [ + 'initial' => [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ], + 'expected' => [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo', + 'icingadb/filter/objects' => 'host.name=foo|hostgroup.name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ] + ] + ]; + + protected $defaultConfigDir; + + protected $fileStorage; + + protected function setUp(): void + { + $this->defaultConfigDir = Config::$configDir; + $this->fileStorage = new TemporaryLocalFileStorage(); + + Config::$configDir = dirname($this->fileStorage->resolvePath('bogus')); + } + + protected function tearDown(): void + { + Config::$configDir = $this->defaultConfigDir; + unset($this->fileStorage); // Should clean up automatically + Config::module('monitoring', 'config', true); + } + + protected function getConfig(string $case): array + { + return [$this->config[$case]['initial'], $this->config[$case]['expected']]; + } + + protected function createConfig(string $path, array $data): void + { + $config = new Config(new ConfigObject($data)); + $config->saveIni($this->fileStorage->resolvePath($path)); + } + + protected function loadConfig(string $path): array + { + return Config::fromIni($this->fileStorage->resolvePath($path))->toArray(); + } + + protected function createCommandInstance(string ...$params): MigrateCommand + { + array_unshift($params, 'program'); + + $app = $this->createConfiguredMock(Cli::class, [ + 'getParams' => new Params($params), + 'getModuleManager' => $this->createConfiguredMock(Manager::class, [ + 'loadEnabledModules' => null + ]) + ]); + + return new MigrateCommand( + $app, + 'migrate', + 'toicingadb', + 'dashboard', + false + ); + } + + /** + * Checks the following: + * - Whether only a single user is handled + * - Whether backups are made + * - Whether a second run changes nothing, if nothing changed + * - Whether a second run keeps the backup, if nothing changed + * - Whether a new backup isn't made, if nothing changed + * - Whether existing Icinga DB dashboards are transformed regarding wildcard filters + */ + public function testDashboardMigrationBehavesAsExpectedByDefault() + { + [$initialConfig, $expected] = $this->getConfig('dashboards'); + + $this->createConfig('dashboards/test/dashboard.ini', $initialConfig); + $this->createConfig('dashboards/test2/dashboard.ini', $initialConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->dashboardAction(); + + $config = $this->loadConfig('dashboards/test/dashboard.ini'); + $this->assertSame($expected, $config); + + $config2 = $this->loadConfig('dashboards/test2/dashboard.ini'); + $this->assertSame($initialConfig, $config2); + + $backup = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $this->assertSame($initialConfig, $backup); + + $command = $this->createCommandInstance('--user', 'test'); + $command->dashboardAction(); + + $configAfterSecondRun = $this->loadConfig('dashboards/test/dashboard.ini'); + $this->assertSame($config, $configAfterSecondRun); + + $backupAfterSecondRun = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $this->assertSame($backup, $backupAfterSecondRun); + + $backup1AfterSecondRun = $this->loadConfig('dashboards/test/dashboard.backup1.ini'); + $this->assertEmpty($backup1AfterSecondRun); + } + + /** + * Checks the following: + * - Whether a second run creates a new backup, if something changed + */ + public function testDashboardMigrationCreatesMultipleBackups() + { + $initialOldConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ] + ]; + $initialNewConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'hosts.group_members' => [ + 'title' => 'Group Members', + 'url' => 'monitoring/list/hosts?hostgroup_name=group1|hostgroup_name=group2' + ] + ]; + $expectedNewConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ] + ]; + $expectedFinalConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'hosts.group_members' => [ + 'title' => 'Group Members', + 'url' => 'icingadb/hosts?hostgroup.name=group1|hostgroup.name=group2' + ] + ]; + + $this->createConfig('dashboards/test/dashboard.ini', $initialOldConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->dashboardAction(); + + $newConfig = $this->loadConfig('dashboards/test/dashboard.ini'); + $this->assertSame($expectedNewConfig, $newConfig); + $oldBackup = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $this->assertSame($initialOldConfig, $oldBackup); + + $this->createConfig('dashboards/test/dashboard.ini', $initialNewConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->dashboardAction(); + + $finalConfig = $this->loadConfig('dashboards/test/dashboard.ini'); + $this->assertSame($expectedFinalConfig, $finalConfig); + $newBackup = $this->loadConfig('dashboards/test/dashboard.backup1.ini'); + $this->assertSame($initialNewConfig, $newBackup); + } + + /** + * Checks the following: + * - Whether backups are skipped + * + * @depends testDashboardMigrationBehavesAsExpectedByDefault + */ + public function testDashboardMigrationSkipsBackupIfRequested() + { + [$initialConfig, $expected] = $this->getConfig('dashboards'); + + $this->createConfig('dashboards/test/dashboard.ini', $initialConfig); + + $command = $this->createCommandInstance('--user', 'test', '--no-backup'); + $command->dashboardAction(); + + $config = $this->loadConfig('dashboards/test/dashboard.ini'); + $this->assertSame($expected, $config); + + $backup = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $this->assertEmpty($backup); + } + + /** + * Checks the following: + * - Whether multiple users are handled + * - Whether multiple backups are made + * + * @depends testDashboardMigrationBehavesAsExpectedByDefault + */ + public function testDashboardMigrationMigratesAllUsers() + { + [$initialConfig, $expected] = $this->getConfig('dashboards'); + + $users = ['foo', 'bar', 'raboof']; + + foreach ($users as $user) { + $this->createConfig("dashboards/$user/dashboard.ini", $initialConfig); + } + + $command = $this->createCommandInstance('--user', '*'); + $command->dashboardAction(); + + foreach ($users as $user) { + $config = $this->loadConfig("dashboards/$user/dashboard.ini"); + $this->assertSame($expected, $config); + + $backup = $this->loadConfig("dashboards/$user/dashboard.backup.ini"); + $this->assertSame($initialConfig, $backup); + } + } + + public function testDashboardMigrationExpectsUserSwitch() + { + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Required parameter \'user\' missing'); + + $command = $this->createCommandInstance(); + $command->dashboardAction(); + } + + /** + * Checks the following: + * - Whether only a single user is handled + * - Whether shared items are migrated, depending on the owner + * - Whether old configs are kept/or backups are created + * - Whether a second run changes nothing, if nothing changed + * - Whether a second run keeps the backup, if nothing changed + * - Whether a new backup isn't created, if nothing changed + */ + public function testNavigationMigrationBehavesAsExpectedByDefault() + { + [$initialMenuConfig, $expectedMenu] = $this->getConfig('menu-items'); + [$initialHostConfig, $expectedHosts] = $this->getConfig('host-actions'); + [$initialServiceConfig, $expectedServices] = $this->getConfig('service-actions'); + + $this->createConfig('preferences/test/menu.ini', $initialMenuConfig); + $this->createConfig('preferences/test/host-actions.ini', $initialHostConfig); + $this->createConfig('preferences/test/service-actions.ini', $initialServiceConfig); + $this->createConfig('preferences/test2/menu.ini', $initialMenuConfig); + $this->createConfig('preferences/test2/host-actions.ini', $initialHostConfig); + $this->createConfig('preferences/test2/service-actions.ini', $initialServiceConfig); + + [$initialSharedMenuConfig, $expectedSharedMenu] = $this->getConfig('shared-menu-items'); + $this->createConfig('navigation/menu.ini', $initialSharedMenuConfig); + + [$initialSharedConfig, $expectedShared] = $this->getConfig('shared-host-actions'); + $this->createConfig('navigation/host-actions.ini', $initialSharedConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $menuConfig = $this->loadConfig('preferences/test/menu.ini'); + $this->assertSame($expectedMenu, $menuConfig); + + $sharedMenuConfig = $this->loadConfig('navigation/menu.ini'); + $this->assertSame($expectedSharedMenu, $sharedMenuConfig); + + $menuConfig2 = $this->loadConfig('preferences/test2/menu.ini'); + $this->assertSame($initialMenuConfig, $menuConfig2); + + $menuBackup = $this->loadConfig('preferences/test/menu.backup.ini'); + $this->assertSame($initialMenuConfig, $menuBackup); + + $hosts = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $services = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($expectedHosts, $hosts); + $this->assertSame($expectedServices, $services); + + $sharedConfig = $this->loadConfig('navigation/icingadb-host-actions.ini'); + $this->assertSame($expectedShared, $sharedConfig); + + $hosts2 = $this->loadConfig('preferences/test2/icingadb-host-actions.ini'); + $services2 = $this->loadConfig('preferences/test2/icingadb-service-actions.ini'); + $this->assertEmpty($hosts2); + $this->assertEmpty($services2); + + $oldHosts = $this->loadConfig('preferences/test/host-actions.ini'); + $oldServices = $this->loadConfig('preferences/test/service-actions.ini'); + $this->assertSame($initialHostConfig, $oldHosts); + $this->assertSame($initialServiceConfig, $oldServices); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $menuConfigAfterSecondRun = $this->loadConfig('preferences/test/menu.ini'); + $this->assertSame($menuConfig, $menuConfigAfterSecondRun); + + $menuBackupAfterSecondRun = $this->loadConfig('preferences/test/menu.backup.ini'); + $this->assertSame($menuBackup, $menuBackupAfterSecondRun); + + $menuBackup1AfterSecondRun = $this->loadConfig('preferences/test/menu.backup1.ini'); + $this->assertEmpty($menuBackup1AfterSecondRun); + + $hostsAfterSecondRun = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $servicesAfterSecondRun = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($hosts, $hostsAfterSecondRun); + $this->assertSame($services, $servicesAfterSecondRun); + } + + /** + * Checks the following: + * - Whether a second run creates a new backup, if something changed + * + * @depends testNavigationMigrationBehavesAsExpectedByDefault + */ + public function testNavigationMigrationCreatesMultipleBackups() + { + $initialOldConfig = [ + 'hosts' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ] + ]; + $initialNewConfig = [ + 'hosts' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'group_members' => [ + 'title' => 'Group Members', + 'url' => 'monitoring/list/hosts?hostgroup_name=group1|hostgroup_name=group2' + ] + ]; + $expectedNewConfig = [ + 'hosts' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ] + ]; + $expectedFinalConfig = [ + 'hosts' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'group_members' => [ + 'title' => 'Group Members', + 'url' => 'icingadb/hosts?hostgroup.name=group1|hostgroup.name=group2' + ] + ]; + + $this->createConfig('preferences/test/menu.ini', $initialOldConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $newConfig = $this->loadConfig('preferences/test/menu.ini'); + $this->assertSame($expectedNewConfig, $newConfig); + $oldBackup = $this->loadConfig('preferences/test/menu.backup.ini'); + $this->assertSame($initialOldConfig, $oldBackup); + + $this->createConfig('preferences/test/menu.ini', $initialNewConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $finalConfig = $this->loadConfig('preferences/test/menu.ini'); + $this->assertSame($expectedFinalConfig, $finalConfig); + $newBackup = $this->loadConfig('preferences/test/menu.backup1.ini'); + $this->assertSame($initialNewConfig, $newBackup); + } + + /** + * Checks the following: + * - Whether backups are skipped + * + * @depends testNavigationMigrationBehavesAsExpectedByDefault + */ + public function testNavigationMigrationSkipsBackupIfRequested() + { + [$initialConfig, $expected] = $this->getConfig('menu-items'); + + $this->createConfig('preferences/test/menu.ini', $initialConfig); + + $command = $this->createCommandInstance('--user', 'test', '--no-backup'); + $command->navigationAction(); + + $config = $this->loadConfig('preferences/test/menu.ini'); + $this->assertSame($expected, $config); + + $backup = $this->loadConfig('preferences/test/menu.backup.ini'); + $this->assertEmpty($backup); + } + + /** + * Checks the following: + * - Whether existing Icinga DB Actions are transformed regarding wildcard filters + */ + public function testNavigationMigrationTransformsAlreadyExistingIcingaDBActions() + { + [$initialHostConfig, $expectedHosts] = $this->getConfig('icingadb-host-actions'); + [$initialServiceConfig, $expectedServices] = $this->getConfig('icingadb-service-actions'); + + $this->createConfig('preferences/test/icingadb-host-actions.ini', $initialHostConfig); + $this->createConfig('preferences/test/icingadb-service-actions.ini', $initialServiceConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $hosts = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $services = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($expectedHosts, $hosts); + $this->assertSame($expectedServices, $services); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $hostsAfterSecondRun = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $servicesAfterSecondRun = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($hosts, $hostsAfterSecondRun); + $this->assertSame($services, $servicesAfterSecondRun); + } + + /** + * Checks the following: + * - Whether legacy host/service macros are migrated + */ + public function testNavigationMigrationMigratesLegacyMacros() + { + [$initialHostConfig, $expectedHosts] = $this->getConfig('host-actions-legacy-macros'); + [$initialServiceConfig, $expectedServices] = $this->getConfig('service-actions-legacy-macros'); + + $this->createConfig('preferences/test/host-actions.ini', $initialHostConfig); + $this->createConfig('preferences/test/service-actions.ini', $initialServiceConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $hosts = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $services = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($expectedHosts, $hosts); + $this->assertSame($expectedServices, $services); + } + + /** + * Checks the following: + * - Whether old configs are removed + */ + public function testNavigationMigrationDeletesOldConfigsIfRequested() + { + [$initialHostConfig, $expectedHosts] = $this->getConfig('host-actions'); + [$initialServiceConfig, $expectedServices] = $this->getConfig('service-actions'); + + $this->createConfig('preferences/test/host-actions.ini', $initialHostConfig); + $this->createConfig('preferences/test/service-actions.ini', $initialServiceConfig); + + $command = $this->createCommandInstance('--user', 'test', '--no-backup'); + $command->navigationAction(); + + $hosts = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $services = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($expectedHosts, $hosts); + $this->assertSame($expectedServices, $services); + + $oldHosts = $this->loadConfig('preferences/test/host-actions.ini'); + $oldServices = $this->loadConfig('preferences/test/service-actions.ini'); + $this->assertEmpty($oldHosts); + $this->assertEmpty($oldServices); + } + + /** + * Checks the following: + * - Whether existing configs are left alone by default + * - Whether existing configs are overridden if requested + */ + public function testNavigationMigrationOverridesExistingActionsIfRequested() + { + $initialOldUserConfig = [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A' + ] + ]; + $initialOldSharedConfig = [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A', + 'owner' => 'test' + ] + ]; + $initialNewUserConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Abar%2A' + ] + ]; + $initialNewSharedConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Abar%2A', + 'owner' => 'test' + ] + ]; + $expectedFinalUserConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A' + ] + ]; + $expectedFinalSharedConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A', + 'owner' => 'test' + ] + ]; + + $this->createConfig('preferences/test/host-actions.ini', $initialOldUserConfig); + $this->createConfig('preferences/test/icingadb-host-actions.ini', $initialNewUserConfig); + $this->createConfig('navigation/host-actions.ini', $initialOldSharedConfig); + $this->createConfig('navigation/icingadb-host-actions.ini', $initialNewSharedConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $finalUserConfig = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $this->assertSame($initialNewUserConfig, $finalUserConfig); + + $finalSharedConfig = $this->loadConfig('navigation/icingadb-host-actions.ini'); + $this->assertSame($initialNewSharedConfig, $finalSharedConfig); + + $command = $this->createCommandInstance('--user', 'test', '--override'); + $command->navigationAction(); + + $finalUserConfig = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $this->assertSame($expectedFinalUserConfig, $finalUserConfig); + + $finalSharedConfig = $this->loadConfig('navigation/icingadb-host-actions.ini'); + $this->assertSame($expectedFinalSharedConfig, $finalSharedConfig); + } + + public function testNavigationMigrationExpectsUserSwitch() + { + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Required parameter \'user\' missing'); + + $command = $this->createCommandInstance(); + $command->navigationAction(); + } + + /** + * Checks the following: + * - Whether only a single role is handled + * - Whether role name matching works + */ + public function testRoleMigrationHandlesASingleRoleOnlyIfRequested() + { + [$initialConfig, $expected] = $this->getConfig('single-role-or-group'); + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', 'one'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expected, $config); + } + + /** + * Checks the following: + * - Whether only a single role is handled + * - Whether group matching works + */ + public function testRoleMigrationHandlesARoleWithMatchingGroups() + { + [$initialConfig, $expected] = $this->getConfig('single-role-or-group'); + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--group', 'support'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expected, $config); + } + + /** + * Checks the following: + * - Whether permissions are properly migrated + * - Whether refusals are properly migrated + * - Whether restrictions are properly migrated + * - Whether blacklists are properly migrated + * - Whether backups are created + * - Whether a second run changes nothing, if nothing changed + * - Whether a second run keeps the backup, if nothing changed + * - Whether a new backup isn't created, if nothing changed + */ + public function testRoleMigrationMigratesAllRoles() + { + [$initialConfig, $expected] = $this->getConfig('all-roles'); + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', '*'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expected, $config); + + $backup = $this->loadConfig('roles.backup.ini'); + $this->assertSame($initialConfig, $backup); + + $command = $this->createCommandInstance('--role', '*'); + $command->roleAction(); + + $configAfterSecondRun = $this->loadConfig('roles.ini'); + $this->assertSame($config, $configAfterSecondRun); + + $backupAfterSecondRun = $this->loadConfig('roles.backup.ini'); + $this->assertSame($backup, $backupAfterSecondRun); + + $backup2 = $this->loadConfig('roles.backup1.ini'); + $this->assertEmpty($backup2); + } + + /** + * Checks the following: + * - Whether backups are skipped + * + * @depends testRoleMigrationMigratesAllRoles + */ + public function testRoleMigrationSkipsBackupIfRequested() + { + [$initialConfig, $expected] = $this->getConfig('all-roles'); + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', '*', '--no-backup'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expected, $config); + + $backup = $this->loadConfig('roles.backup.ini'); + $this->assertEmpty($backup); + } + + /** + * Checks the following: + * - Whether monitoring's variable protection rules are migrated to all roles granting access to monitoring + */ + public function testRoleMigrationAlsoMigratesVariableProtections() + { + $initialConfig = [ + 'one' => [ + 'permissions' => 'module/monitoring' + ], + 'two' => [ + 'permissions' => 'module/monitoring' + ], + 'three' => [ + 'permissions' => 'module/reporting' + ] + ]; + $expectedConfig = [ + 'one' => [ + 'permissions' => 'module/monitoring', + 'icingadb/protect/variables' => 'ob.*,env' + ], + 'two' => [ + 'permissions' => 'module/monitoring', + 'icingadb/protect/variables' => 'ob.*,env' + ], + 'three' => [ + 'permissions' => 'module/reporting' + ] + ]; + + $this->createConfig('modules/monitoring/config.ini', [ + 'security' => [ + 'protected_customvars' => 'ob.*,env' + ] + ]); + + // Invalidate config cache + Config::module('monitoring', 'config', true); + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', '*'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expectedConfig, $config); + } + + /** + * Checks the following: + * - Whether already migrated roles are skipped during migration + * - Whether already migrated roles are transformed regarding wildcard filters + */ + public function testRoleMigrationSkipsRolesThatAlreadyGrantAccessToIcingaDbButTransformWildcardRestrictions() + { + $initialConfig = [ + 'only-monitoring' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*', + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A' + ], + 'monitoring-and-icingadb' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*,module/icingadb', + 'monitoring/filter/objects' => 'host_name=%2Abar%2A', + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedConfig = [ + 'only-monitoring' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*,icingadb/command/comment/*', + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A', + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A' + ], + 'monitoring-and-icingadb' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*,module/icingadb', + 'monitoring/filter/objects' => 'host_name=%2Abar%2A', + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A' + ] + ]; + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', '*'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expectedConfig, $config); + } + + /** + * Checks the following: + * - Whether already migrated roles are reset if requested + */ + public function testRoleMigrationOverridesAlreadyMigratedRolesIfRequested() + { + $initialConfig = [ + 'only-monitoring' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*', + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A' + ], + 'monitoring-and-icingadb' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*,module/icingadb', + 'monitoring/filter/objects' => 'host_name=%2Abar%2A', + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedConfig = [ + 'only-monitoring' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*,icingadb/command/comment/*', + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A', + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A' + ], + 'monitoring-and-icingadb' => [ + 'permissions' => 'module/monitoring' + . ',monitoring/command/comment/*' + . ',icingadb/command/comment/*', + 'monitoring/filter/objects' => 'host_name=%2Abar%2A', + 'icingadb/filter/objects' => 'host.name~%2Abar%2A' + ] + ]; + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', '*', '--override'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expectedConfig, $config); + } + + public function testRoleMigrationExpectsTheRoleOrGroupSwitch() + { + $this->expectException(IcingaException::class); + $this->expectExceptionMessage("One of the parameters 'group' or 'role' must be supplied"); + + $command = $this->createCommandInstance(); + $command->roleAction(); + } + + public function testRoleMigrationExpectsEitherTheRoleOrGroupSwitchButNotBoth() + { + $this->expectException(IcingaException::class); + $this->expectExceptionMessage("Use either 'group' or 'role'. Both cannot be used as role overrules group."); + + $command = $this->createCommandInstance('--role=foo', '--group=bar'); + $command->roleAction(); + } + + public function testFilterMigrationWorksAsExpected() + { + $initialHostActionConfig = [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A' + ] + ]; + $expectedHostActionConfig = $initialHostActionConfig; + + $initialIcingadbHostActionConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedIcingadbHostActionConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A' + ] + ]; + + $initialServiceActionConfig = [ + 'services' => [ + 'type' => 'service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => '_service_foo=bar&_service_bar=%2Afoo%2A' + ] + ]; + $expectedServiceActionConfig = $initialServiceActionConfig; + + $initialIcingadbServiceActionConfig = [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar=%2Afoo%2A' + ] + ]; + $expectedIcingadbServiceActionConfig = [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar~%2Afoo%2A' + ] + ]; + + $initialMenuConfig = [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A' + ] + ]; + $expectedMenuConfig = [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A' + ] + ]; + + $initialDashboardConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name=%2Alinux%2A' + ] + ]; + $expectedDashboardConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name~%2Alinux%2A' + ] + ]; + + $initialRoleConfig = [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedRoleConfig = [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A' + ] + ]; + + $this->createConfig('preferences/test/host-actions.ini', $initialHostActionConfig); + $this->createConfig('preferences/test/service-actions.ini', $initialServiceActionConfig); + $this->createConfig('preferences/test/icingadb-host-actions.ini', $initialIcingadbHostActionConfig); + $this->createConfig('preferences/test/icingadb-service-actions.ini', $initialIcingadbServiceActionConfig); + $this->createConfig('dashboards/test/dashboard.ini', $initialDashboardConfig); + $this->createConfig('preferences/test/menu.ini', $initialMenuConfig); + $this->createConfig('roles.ini', $initialRoleConfig); + + $command = $this->createCommandInstance(); + $command->filterAction(); + + $hostActionConfig = $this->loadConfig('preferences/test/host-actions.ini'); + $serviceActionConfig = $this->loadConfig('preferences/test/service-actions.ini'); + $icingadbHostActionConfig = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $icingadbServiceActionConfig = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $dashboardBackup = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $dashboardConfig = $this->loadConfig('dashboards/test/dashboard.ini'); + $menuBackup = $this->loadConfig('preferences/test/menu.backup.ini'); + $menuConfig = $this->loadConfig('preferences/test/menu.ini'); + $roleBackup = $this->loadConfig('roles.backup.ini'); + $roleConfig = $this->loadConfig('roles.ini'); + + $this->assertSame($expectedHostActionConfig, $hostActionConfig); + $this->assertSame($expectedServiceActionConfig, $serviceActionConfig); + $this->assertSame($initialDashboardConfig, $dashboardBackup); + $this->assertSame($initialMenuConfig, $menuBackup); + $this->assertSame($initialRoleConfig, $roleBackup); + + $this->assertSame($expectedIcingadbHostActionConfig, $icingadbHostActionConfig); + $this->assertSame($expectedIcingadbServiceActionConfig, $icingadbServiceActionConfig); + $this->assertSame($expectedDashboardConfig, $dashboardConfig); + $this->assertSame($expectedMenuConfig, $menuConfig); + $this->assertSame($expectedRoleConfig, $roleConfig); + } + + /** + * @depends testFilterMigrationWorksAsExpected + */ + public function testFilterMigrationSkipsBackupsIfRequested() + { + $initialHostActionConfig = [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A' + ] + ]; + $expectedHostActionConfig = $initialHostActionConfig; + + $initialIcingadbHostActionConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedIcingadbHostActionConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A' + ] + ]; + + $initialServiceActionConfig = [ + 'services' => [ + 'type' => 'service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => '_service_foo=bar&_service_bar=%2Afoo%2A' + ] + ]; + $expectedServiceActionConfig = $initialServiceActionConfig; + + $initialIcingadbServiceActionConfig = [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar=%2Afoo%2A' + ] + ]; + $expectedIcingadbServiceActionConfig = [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar~%2Afoo%2A' + ] + ]; + + $initialMenuConfig = [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A' + ] + ]; + $expectedMenuConfig = [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A' + ] + ]; + + $initialDashboardConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name=%2Alinux%2A' + ] + ]; + $expectedDashboardConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name~%2Alinux%2A' + ] + ]; + + $initialRoleConfig = [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedRoleConfig = [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A' + ] + ]; + + $this->createConfig('preferences/test/host-actions.ini', $initialHostActionConfig); + $this->createConfig('preferences/test/service-actions.ini', $initialServiceActionConfig); + $this->createConfig('preferences/test/icingadb-host-actions.ini', $initialIcingadbHostActionConfig); + $this->createConfig('preferences/test/icingadb-service-actions.ini', $initialIcingadbServiceActionConfig); + $this->createConfig('dashboards/test/dashboard.ini', $initialDashboardConfig); + $this->createConfig('preferences/test/menu.ini', $initialMenuConfig); + $this->createConfig('roles.ini', $initialRoleConfig); + + $command = $this->createCommandInstance('--no-backup'); + $command->filterAction(); + + $hostActionConfig = $this->loadConfig('preferences/test/host-actions.ini'); + $serviceActionConfig = $this->loadConfig('preferences/test/service-actions.ini'); + $icingadbHostActionConfig = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $icingadbServiceActionConfig = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $dashboardBackup = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $dashboardConfig = $this->loadConfig('dashboards/test/dashboard.ini'); + $menuBackup = $this->loadConfig('preferences/test/menu.backup.ini'); + $menuConfig = $this->loadConfig('preferences/test/menu.ini'); + $roleBackup = $this->loadConfig('roles.backup.ini'); + $roleConfig = $this->loadConfig('roles.ini'); + + $this->assertSame($expectedHostActionConfig, $hostActionConfig); + $this->assertSame($expectedServiceActionConfig, $serviceActionConfig); + $this->assertEmpty($dashboardBackup); + $this->assertEmpty($menuBackup); + $this->assertEmpty($roleBackup); + + $this->assertSame($expectedIcingadbHostActionConfig, $icingadbHostActionConfig); + $this->assertSame($expectedIcingadbServiceActionConfig, $icingadbServiceActionConfig); + $this->assertSame($expectedDashboardConfig, $dashboardConfig); + $this->assertSame($expectedMenuConfig, $menuConfig); + $this->assertSame($expectedRoleConfig, $roleConfig); + } + + public function testNavigationMigrationWorksEvenIfOnlySharedItemsExist() + { + $this->expectNotToPerformAssertions(); + + $this->createConfig('navigation/menu.ini', []); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + } + + public function testNavigationMigrationWorksEvenIfOnlyUserItemsExist() + { + $this->expectNotToPerformAssertions(); + + $this->createConfig('preferences/test/menu.ini', []); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + } + + public function testDashboardMigrationWorksEvenIfNoDashboardsExist() + { + $this->expectNotToPerformAssertions(); + + $command = $this->createCommandInstance('--user', 'test'); + $command->dashboardAction(); + } +}