From 178d5f8283c7d597cf729c6843926b99de087266 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 7 Jun 2017 16:25:35 +0200 Subject: [PATCH] Add CLI command for migrating users according to a given domain refs #2153 --- .../application/clicommands/ConfigCommand.php | 119 ++++++ .../Migrate/Config/UserDomainMigration.php | 393 ++++++++++++++++++ 2 files changed, 512 insertions(+) create mode 100644 modules/migrate/application/clicommands/ConfigCommand.php create mode 100644 modules/migrate/library/Migrate/Config/UserDomainMigration.php diff --git a/modules/migrate/application/clicommands/ConfigCommand.php b/modules/migrate/application/clicommands/ConfigCommand.php new file mode 100644 index 000000000..a5be144a1 --- /dev/null +++ b/modules/migrate/application/clicommands/ConfigCommand.php @@ -0,0 +1,119 @@ + The new domain for the users + * + * --from-domain= Migrate only the users with the given domain. + * Use this switch in combination with --to-domain. + * + * --user= Migrate only the given user in the format or + * + * --map-file= File to use for renaming users + * + * --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 . 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/library/Migrate/Config/UserDomainMigration.php b/modules/migrate/library/Migrate/Config/UserDomainMigration.php new file mode 100644 index 000000000..f7e52c438 --- /dev/null +++ b/modules/migrate/library/Migrate/Config/UserDomainMigration.php @@ -0,0 +1,393 @@ +map = $map; + + return $static; + } + + public static function fromDomains($toDomain, $fromDomain = null) + { + $static = new static(); + + $static->toDomain = $toDomain; + $static->fromDomain = $fromDomain; + + return $static; + } + + protected function mustMigrate(User $user) + { + if ($user->getUsername() === '*') { + return false; + } + + if ($this->map !== null) { + return isset($this->map[$user->getUsername()]); + } + + if ($this->fromDomain !== null && $user->hasDomain() && $user->getDomain() !== $this->fromDomain) { + return false; + } + + return true; + } + + protected function migrateUser(User $user) + { + $migrated = clone $user; + + if ($this->map !== null) { + $migrated->setUsername($this->map[$user->getUsername()]); + } else { + $migrated->setDomain($this->toDomain); + } + + return $migrated; + } + + protected function migrateAnnounces() + { + $announces = new AnnouncementIniRepository(); + + $query = $announces->select(array('author')); + + if ($this->map !== null) { + $query->where('author', array_keys($this->map)); + } + + $migratedUsers = array(); + + foreach ($announces->select(array('author')) as $announce) { + $user = new User($announce->author); + + if (! $this->mustMigrate($user)) { + continue; + } + + if (isset($migratedUsers[$user->getUsername()])) { + continue; + } + + $migrated = $this->migrateUser($user); + + $announces->update( + 'announcement', + array('author' => $migrated->getUsername()), + Filter::where('author', $user->getUsername()) + ); + + $migratedUsers[$user->getUsername()] = true; + } + } + + protected function migrateDashboards() + { + $directory = Config::resolvePath('dashboards'); + + $migration = array(); + + if (DirectoryIterator::isReadable($directory)) { + foreach (new DirectoryIterator($directory) as $username => $path) { + $user = new User($username); + + if (! $this->mustMigrate($user)) { + continue; + } + + $migrated = $this->migrateUser($user); + + $migration[$path] = dirname($path) . '/' . $migrated->getUsername(); + } + + foreach ($migration as $from => $to) { + rename($from, $to); + } + } + + } + + protected function migrateNavigation() + { + $directory = Config::resolvePath('navigation'); + + foreach (new DirectoryIterator($directory, 'ini') as $file) { + $config = Config::fromIni($file); + + foreach ($config as $navigation) { + $owner = $navigation->owner; + + if (! empty($owner)) { + $user = new User($owner); + + if ($this->mustMigrate($user)) { + $migrated = $this->migrateUser($user); + + $navigation->owner = $migrated->getUsername(); + } + } + + $users = $navigation->users; + + if (! empty($users)) { + $users = StringHelper::trimSplit($users); + + foreach ($users as &$username) { + $user = new User($username); + + if (! $this->mustMigrate($user)) { + continue; + } + + $migrated = $this->migrateUser($user); + + $username = $migrated->getUsername(); + } + + $navigation->users = implode(',', $users); + } + } + + $config->saveIni(); + } + } + + protected function migratePreferences() + { + $config = Config::app(); + + $type = $config->get('global', 'config_backend', 'ini'); + + switch ($type) { + case 'ini': + $directory = Config::resolvePath('preferences'); + + $migration = array(); + + if (DirectoryIterator::isReadable($directory)) { + foreach (new DirectoryIterator($directory) as $username => $path) { + $user = new User($username); + + if (! $this->mustMigrate($user)) { + continue; + } + + $migrated = $this->migrateUser($user); + + $migration[$path] = dirname($path) . '/' . $migrated->getUsername(); + } + + foreach ($migration as $from => $to) { + rename($from, $to); + } + } + + break; + case 'db': + /** @var DbConnection $conn */ + $conn = ResourceFactory::create($config->get('global', 'config_resource')); + + $query = $conn + ->select() + ->from('icingaweb_user_preference', array('username')) + ->group('username'); + + if ($this->map !== null) { + $query->applyFilter(Filter::matchAny(Filter::where('username', array_keys($this->map)))); + } + + $users = $query->fetchColumn(); + + $migration = array(); + + foreach ($users as $username) { + $user = new User($username); + + if (! $this->mustMigrate($user)) { + continue; + } + + $migrated = $this->migrateUser($user); + + $migration[$username] = $migrated->getUsername(); + } + + if (! empty($migration)) { + $conn->getDbAdapter()->beginTransaction(); + + foreach ($migration as $originalUsername => $username) { + $conn->update( + 'icingaweb_user_preference', + array('username' => $username), + Filter::where('username', $originalUsername) + ); + } + + $conn->getDbAdapter()->commit(); + } + } + } + + protected function migrateRoles() + { + $roles = Config::app('roles'); + + foreach ($roles as $role) { + $users = $role->users; + + if (empty($users)) { + continue; + } + + $users = StringHelper::trimSplit($users); + + foreach ($users as &$username) { + $user = new User($username); + + if (! $this->mustMigrate($user)) { + continue; + } + + $migrated = $this->migrateUser($user); + + $username = $migrated->getUsername(); + } + + $role->users = implode(',', $users); + } + + $roles->saveIni(); + } + + protected function migrateUsers() + { + foreach (Config::app('authentication') as $name => $config) { + if (strtolower($config->backend) !== 'db') { + continue; + } + + /** @var DbConnection $conn */ + $conn = ResourceFactory::create($config->resource); + + $query = $conn + ->select() + ->from('icingaweb_user', array('name')) + ->group('name'); + + if ($this->map !== null) { + $query->applyFilter(Filter::matchAny(Filter::where('name', array_keys($this->map)))); + } + + $users = $query->fetchColumn(); + + $migration = array(); + + foreach ($users as $username) { + $user = new User($username); + + if (! $this->mustMigrate($user)) { + continue; + } + + $migrated = $this->migrateUser($user); + + $migration[$username] = $migrated->getUsername(); + } + + if (! empty($migration)) { + $conn->getDbAdapter()->beginTransaction(); + + foreach ($migration as $originalUsername => $username) { + $conn->update( + 'icingaweb_user', + array('name' => $username), + Filter::where('name', $originalUsername) + ); + } + + $conn->getDbAdapter()->commit(); + } + } + + foreach (Config::app('groups') as $name => $config) { + if (strtolower($config->backend) !== 'db') { + continue; + } + + /** @var DbConnection $conn */ + $conn = ResourceFactory::create($config->resource); + + $query = $conn + ->select() + ->from('icingaweb_group_membership', array('username')) + ->group('username'); + + if ($this->map !== null) { + $query->applyFilter(Filter::matchAny(Filter::where('username', array_keys($this->map)))); + } + + $users = $query->fetchColumn(); + + $migration = array(); + + foreach ($users as $username) { + $user = new User($username); + + if (! $this->mustMigrate($user)) { + continue; + } + + $migrated = $this->migrateUser($user); + + $migration[$username] = $migrated->getUsername(); + } + + if (! empty($migration)) { + $conn->getDbAdapter()->beginTransaction(); + + foreach ($migration as $originalUsername => $username) { + $conn->update( + 'icingaweb_group_membership', + array('username' => $username), + Filter::where('username', $originalUsername) + ); + } + + $conn->getDbAdapter()->commit(); + } + } + } + + public function migrate() + { + $this->migrateAnnounces(); + $this->migrateDashboards(); + $this->migrateNavigation(); + $this->migratePreferences(); + $this->migrateRoles(); + $this->migrateUsers(); + } +}