diff options
author | Côme Chilliet <91878298+come-nc@users.noreply.github.com> | 2022-03-22 09:11:28 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-22 09:11:28 +0100 |
commit | 58582dfd33300e7ef9808dc209544837b1075742 (patch) | |
tree | 624ef1bc70259fd3d783a56d16084e10196e07f0 /apps | |
parent | cabe6f383e6932be3e0b07e6de5a23ba51b88723 (diff) | |
parent | 85123f9d36bf9c48c01e6af2b3c85a81b3670c0a (diff) | |
download | nextcloud-server-58582dfd33300e7ef9808dc209544837b1075742.tar.gz nextcloud-server-58582dfd33300e7ef9808dc209544837b1075742.zip |
Merge pull request #31382 from nextcloud/feat/account-migrator
User account migration
Diffstat (limited to 'apps')
5 files changed, 179 insertions, 0 deletions
diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php index 3d3729a66e5..bd092f3b401 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -74,5 +74,7 @@ return array( 'OCA\\Settings\\SetupChecks\\PhpDefaultCharset' => $baseDir . '/../lib/SetupChecks/PhpDefaultCharset.php', 'OCA\\Settings\\SetupChecks\\PhpOutputBuffering' => $baseDir . '/../lib/SetupChecks/PhpOutputBuffering.php', 'OCA\\Settings\\SetupChecks\\SupportedDatabase' => $baseDir . '/../lib/SetupChecks/SupportedDatabase.php', + 'OCA\\Settings\\UserMigration\\AccountMigrator' => $baseDir . '/../lib/UserMigration/AccountMigrator.php', + 'OCA\\Settings\\UserMigration\\AccountMigratorException' => $baseDir . '/../lib/UserMigration/AccountMigratorException.php', 'OCA\\Settings\\WellKnown\\SecurityTxtHandler' => $baseDir . '/../lib/WellKnown/SecurityTxtHandler.php', ); diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php index 7d00184dc7f..b8aec66c25b 100644 --- a/apps/settings/composer/composer/autoload_static.php +++ b/apps/settings/composer/composer/autoload_static.php @@ -89,6 +89,8 @@ class ComposerStaticInitSettings 'OCA\\Settings\\SetupChecks\\PhpDefaultCharset' => __DIR__ . '/..' . '/../lib/SetupChecks/PhpDefaultCharset.php', 'OCA\\Settings\\SetupChecks\\PhpOutputBuffering' => __DIR__ . '/..' . '/../lib/SetupChecks/PhpOutputBuffering.php', 'OCA\\Settings\\SetupChecks\\SupportedDatabase' => __DIR__ . '/..' . '/../lib/SetupChecks/SupportedDatabase.php', + 'OCA\\Settings\\UserMigration\\AccountMigrator' => __DIR__ . '/..' . '/../lib/UserMigration/AccountMigrator.php', + 'OCA\\Settings\\UserMigration\\AccountMigratorException' => __DIR__ . '/..' . '/../lib/UserMigration/AccountMigratorException.php', 'OCA\\Settings\\WellKnown\\SecurityTxtHandler' => __DIR__ . '/..' . '/../lib/WellKnown/SecurityTxtHandler.php', ); diff --git a/apps/settings/lib/AppInfo/Application.php b/apps/settings/lib/AppInfo/Application.php index 4810e78db2b..89a988621f2 100644 --- a/apps/settings/lib/AppInfo/Application.php +++ b/apps/settings/lib/AppInfo/Application.php @@ -46,6 +46,7 @@ use OCA\Settings\Mailer\NewUserMailHelper; use OCA\Settings\Middleware\SubadminMiddleware; use OCA\Settings\Search\AppSearch; use OCA\Settings\Search\SectionSearch; +use OCA\Settings\UserMigration\AccountMigrator; use OCA\Settings\WellKnown\SecurityTxtHandler; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; @@ -131,6 +132,8 @@ class Application extends App implements IBootstrap { Util::getDefaultEmailAddress('no-reply') ); }); + + $context->registerUserMigrator(AccountMigrator::class); } public function boot(IBootContext $context): void { diff --git a/apps/settings/lib/UserMigration/AccountMigrator.php b/apps/settings/lib/UserMigration/AccountMigrator.php new file mode 100644 index 00000000000..e50a01e515f --- /dev/null +++ b/apps/settings/lib/UserMigration/AccountMigrator.php @@ -0,0 +1,140 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2022 Christopher Ng <chrng8@gmail.com> + * + * @author Christopher Ng <chrng8@gmail.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Settings\UserMigration; + +use InvalidArgumentException; +use OC\Accounts\TAccountsHelper; +use OC\NotSquareException; +use OCA\Settings\AppInfo\Application; +use OCP\Accounts\IAccountManager; +use OCP\IAvatarManager; +use OCP\IUser; +use OCP\UserMigration\IExportDestination; +use OCP\UserMigration\IImportSource; +use OCP\UserMigration\IMigrator; +use OCP\UserMigration\TMigratorBasicVersionHandling; +use Symfony\Component\Console\Output\OutputInterface; +use Throwable; + +class AccountMigrator implements IMigrator { + use TMigratorBasicVersionHandling; + + use TAccountsHelper; + + private IAccountManager $accountManager; + + private IAvatarManager $avatarManager; + + private const PATH_ROOT = Application::APP_ID . '/'; + + private const PATH_ACCOUNT_FILE = AccountMigrator::PATH_ROOT . 'account.json'; + + private const AVATAR_BASENAME = 'avatar'; + + public function __construct( + IAccountManager $accountManager, + IAvatarManager $avatarManager + ) { + $this->accountManager = $accountManager; + $this->avatarManager = $avatarManager; + } + + /** + * {@inheritDoc} + */ + public function export(IUser $user, IExportDestination $exportDestination, OutputInterface $output): void { + $output->writeln('Exporting account information in ' . AccountMigrator::PATH_ACCOUNT_FILE . '…'); + + $account = $this->accountManager->getAccount($user); + if ($exportDestination->addFileContents(AccountMigrator::PATH_ACCOUNT_FILE, json_encode($account)) === false) { + throw new AccountMigratorException('Could not export account information'); + } + + $avatar = $this->avatarManager->getAvatar($user->getUID()); + if ($avatar->isCustomAvatar()) { + $avatarFile = $avatar->getFile(-1); + $exportPath = AccountMigrator::PATH_ROOT . AccountMigrator::AVATAR_BASENAME . '.' . $avatarFile->getExtension(); + + $output->writeln('Exporting avatar to ' . $exportPath . '…'); + if ($exportDestination->addFileAsStream($exportPath, $avatarFile->read()) === false) { + throw new AccountMigratorException('Could not export avatar'); + } + } + } + + /** + * {@inheritDoc} + */ + public function import(IUser $user, IImportSource $importSource, OutputInterface $output): void { + if ($importSource->getMigratorVersion(static::class) === null) { + $output->writeln('No version for ' . static::class . ', skipping import…'); + return; + } + + $output->writeln('Importing account information from ' . AccountMigrator::PATH_ACCOUNT_FILE . '…'); + + $account = $this->accountManager->getAccount($user); + + /** @var array<string, array<string, string>>|array<string, array<int, array<string, string>>> $data */ + $data = json_decode($importSource->getFileContents(AccountMigrator::PATH_ACCOUNT_FILE), true, 512, JSON_THROW_ON_ERROR); + $account->setAllPropertiesFromJson($data); + + try { + $this->accountManager->updateAccount($account); + } catch (InvalidArgumentException $e) { + throw new AccountMigratorException('Failed to import account information'); + } + + /** @var array<int, string> $avatarFiles */ + $avatarFiles = array_filter( + $importSource->getFolderListing(AccountMigrator::PATH_ROOT), + fn (string $filename) => pathinfo($filename, PATHINFO_FILENAME) === AccountMigrator::AVATAR_BASENAME, + ); + + if (!empty($avatarFiles)) { + if (count($avatarFiles) > 1) { + $output->writeln('Expected single avatar image file, using first file found'); + } + + $importPath = AccountMigrator::PATH_ROOT . reset($avatarFiles); + + $output->writeln('Importing avatar from ' . $importPath . '…'); + $stream = $importSource->getFileAsStream($importPath); + $image = new \OC_Image(); + $image->loadFromFileHandle($stream); + + try { + $avatar = $this->avatarManager->getAvatar($user->getUID()); + $avatar->set($image); + } catch (NotSquareException $e) { + throw new AccountMigratorException('Avatar image must be square'); + } catch (Throwable $e) { + throw new AccountMigratorException('Failed to import avatar', 0, $e); + } + } + } +} diff --git a/apps/settings/lib/UserMigration/AccountMigratorException.php b/apps/settings/lib/UserMigration/AccountMigratorException.php new file mode 100644 index 00000000000..91c63ba9d80 --- /dev/null +++ b/apps/settings/lib/UserMigration/AccountMigratorException.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2022 Christopher Ng <chrng8@gmail.com> + * + * @author Christopher Ng <chrng8@gmail.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Settings\UserMigration; + +use OCP\UserMigration\UserMigrationException; + +class AccountMigratorException extends UserMigrationException { +} |