diff options
Diffstat (limited to '')
3 files changed, 270 insertions, 0 deletions
diff --git a/modules/migrate/application/clicommands/ConfigCommand.php b/modules/migrate/application/clicommands/ConfigCommand.php new file mode 100644 index 0000000..a5be144 --- /dev/null +++ b/modules/migrate/application/clicommands/ConfigCommand.php @@ -0,0 +1,119 @@ +<?php +/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Module\Migrate\Clicommands; + +use Icinga\Cli\Command; +use Icinga\Module\Migrate\Config\UserDomainMigration; +use Icinga\User; +use Icinga\Util\StringHelper; + +class ConfigCommand extends Command +{ + /** + * Rename users and user configurations according to a given domain + * + * The following configurations are taken into account: + * - Announcements + * - Preferences + * - Dashboards + * - Custom navigation items + * - Role configuration + * - Users and group memberships in database backends, if configured + * + * USAGE: + * + * icingacli migrate config users [options] + * + * OPTIONS: + * + * --to-domain=<to-domain> The new domain for the users + * + * --from-domain=<from-domain> Migrate only the users with the given domain. + * Use this switch in combination with --to-domain. + * + * --user=<user> Migrate only the given user in the format <user> or <user@domain> + * + * --map-file=<mapfile> File to use for renaming users + * + * --separator=<separator> Separator for the map file + * + * EXAMPLES: + * + * icingacli migrate config users ... + * + * Add the domain "icinga.com" to all users: + * + * --to-domain icinga.com + * + * Set the domain "example.com" on all users that have the domain "icinga.com": + * + * --to-domain example.com --from-domain icinga.com + * + * Set the domain "icinga.com" on the user "icingaadmin": + * + * --to-domain icinga.com --user icingaadmin + * + * Set the domain "icinga.com" on the users "icingaadmin@icinga.com" + * + * --to-domain example.com --user icingaadmin@icinga.com + * + * Rename users according to a map file: + * + * --map-file /path/to/mapfile --separator : + * + * MAPFILE: + * + * You may rename users according to a given map file. The map file must be separated by newlines. Each line then + * is specified in the format <from><separator><to>. The separator is specified with the --separator switch. + * + * Example content: + * + * icingaadmin:icingaadmin@icinga.com + * jdoe@example.com:jdoe@icinga.com + * rroe@icinga:rroe@icinga.com + */ + public function usersAction() + { + if ($this->params->has('map-file')) { + $mapFile = $this->params->get('map-file'); + $separator = $this->params->getRequired('separator'); + + $source = trim(file_get_contents($mapFile)); + $source = StringHelper::trimSplit($source, "\n"); + + $map = array(); + + array_walk($source, function ($item) use ($separator, &$map) { + list($from, $to) = StringHelper::trimSplit($item, $separator, 2); + $map[$from] = $to; + }); + + $migration = UserDomainMigration::fromMap($map); + } else { + $toDomain = $this->params->getRequired('to-domain'); + $fromDomain = $this->params->get('from-domain'); + $user = $this->params->get('user'); + + if ($user === null) { + $migration = UserDomainMigration::fromDomains($toDomain, $fromDomain); + } else { + if ($fromDomain !== null) { + $this->fail( + "Ambiguous arguments: Can't use --user in combination with --from-domain." + . " Please use the user@domain syntax for the --user switch instead." + ); + } + + $user = new User($user); + + $migrated = clone $user; + $migrated->setDomain($toDomain); + + $migration = UserDomainMigration::fromMap(array($user->getUsername() => $migrated->getUsername())); + } + } + + $migration->migrate(); + } +} diff --git a/modules/migrate/application/clicommands/NavigationCommand.php b/modules/migrate/application/clicommands/NavigationCommand.php new file mode 100644 index 0000000..28ae8a1 --- /dev/null +++ b/modules/migrate/application/clicommands/NavigationCommand.php @@ -0,0 +1,20 @@ +<?php + +/* Icinga Web 2 | (c) 2021 Icinga GmbH | GPLv2+ */ + +namespace Icinga\Module\Migrate\Clicommands; + +use Icinga\Application\Logger; +use Icinga\Cli\Command; + +class NavigationCommand extends Command +{ + /** + * Deprecated. Use `icingacli icingadb migrate navigation` instead. + */ + public function indexAction(): void + { + Logger::error('Deprecated. Use `icingacli icingadb migrate navigation` instead.'); + exit(1); + } +} diff --git a/modules/migrate/application/clicommands/PreferencesCommand.php b/modules/migrate/application/clicommands/PreferencesCommand.php new file mode 100644 index 0000000..11d1edb --- /dev/null +++ b/modules/migrate/application/clicommands/PreferencesCommand.php @@ -0,0 +1,131 @@ +<?php +/* Icinga Web 2 | (c) 2021 Icinga GmbH | GPLv2+ */ + +namespace Icinga\Module\Migrate\Clicommands; + +use Icinga\Application\Config; +use Icinga\Application\Logger; +use Icinga\Cli\Command; +use Icinga\Data\ConfigObject; +use Icinga\Data\ResourceFactory; +use Icinga\Exception\NotReadableError; +use Icinga\Exception\NotWritableError; +use Icinga\File\Ini\IniParser; +use Icinga\User; +use Icinga\User\Preferences\PreferencesStore; +use Icinga\Util\DirectoryIterator; + +class PreferencesCommand extends Command +{ + /** + * Migrate local INI user preferences to a database + * + * USAGE + * + * icingacli migrate preferences [options] + * + * OPTIONS: + * + * --resource=<resource-name> The resource to use, if no current database config backend is configured. + * --no-set-config-backend Do not set the given resource as config backend automatically + */ + public function indexAction() + { + $resource = Config::app()->get('global', 'config_resource'); + if (empty($resource)) { + $resource = $this->params->getRequired('resource'); + } + + $resourceConfig = ResourceFactory::getResourceConfig($resource); + if ($resourceConfig->db === 'mysql') { + $resourceConfig->charset = 'utf8mb4'; + } + + $connection = ResourceFactory::createResource($resourceConfig); + + $preferencesPath = Config::resolvePath('preferences'); + if (! file_exists($preferencesPath)) { + Logger::info('There are no local user preferences to migrate'); + return; + } + + $rc = 0; + + $preferenceDirs = new DirectoryIterator($preferencesPath); + foreach ($preferenceDirs as $preferenceDir) { + if (! is_dir($preferenceDir)) { + continue; + } + + $userName = basename($preferenceDir); + + Logger::info('Migrating INI preferences for user "%s" to database...', $userName); + + $dbStore = new PreferencesStore(new ConfigObject(['connection' => $connection]), new User($userName)); + + try { + $dbStore->load(); + $dbStore->save( + new User\Preferences( + $this->loadIniFile($preferencesPath, (new User($userName))->getUsername()) + ) + ); + } catch (NotReadableError $e) { + if ($e->getPrevious() !== null) { + Logger::error('%s: %s', $e->getMessage(), $e->getPrevious()->getMessage()); + } else { + Logger::error($e->getMessage()); + } + + $rc = 128; + } catch (NotWritableError $e) { + Logger::error('%s: %s', $e->getMessage(), $e->getPrevious()->getMessage()); + $rc = 256; + } + } + + if ($rc > 0) { + Logger::error('Failed to migrate some user preferences'); + exit($rc); + } + + if ($this->params->has('resource') && ! $this->params->has('no-set-config-backend')) { + $appConfig = Config::app(); + $globalConfig = $appConfig->getSection('global'); + $globalConfig['config_resource'] = $resource; + + try { + $appConfig->saveIni(); + } catch (NotWritableError $e) { + Logger::error('Failed to update general configuration: %s', $e->getMessage()); + exit(256); + } + } + + Logger::info('Successfully migrated all local user preferences to database'); + } + + private function loadIniFile(string $filePath, string $username): array + { + $preferences = []; + $preferencesFile = sprintf( + '%s/%s/config.ini', + $filePath, + strtolower($username) + ); + + if (file_exists($preferencesFile)) { + if (! is_readable($preferencesFile)) { + throw new NotReadableError( + 'Preferences INI file %s for user %s is not readable', + $preferencesFile, + $username + ); + } else { + $preferences = IniParser::parseIniFile($preferencesFile)->toArray(); + } + } + + return $preferences; + } +} |