diff options
Diffstat (limited to 'core/Command/User')
-rw-r--r-- | core/Command/User/Add.php | 6 | ||||
-rw-r--r-- | core/Command/User/AuthTokens/Add.php | 4 | ||||
-rw-r--r-- | core/Command/User/AuthTokens/Delete.php | 1 | ||||
-rw-r--r-- | core/Command/User/AuthTokens/ListCommand.php | 1 | ||||
-rw-r--r-- | core/Command/User/Info.php | 4 | ||||
-rw-r--r-- | core/Command/User/LastSeen.php | 2 | ||||
-rw-r--r-- | core/Command/User/ListCommand.php | 1 | ||||
-rw-r--r-- | core/Command/User/Profile.php | 234 | ||||
-rw-r--r-- | core/Command/User/ResetPassword.php | 6 | ||||
-rw-r--r-- | core/Command/User/SyncAccountDataCommand.php | 10 | ||||
-rw-r--r-- | core/Command/User/Welcome.php | 14 |
11 files changed, 255 insertions, 28 deletions
diff --git a/core/Command/User/Add.php b/core/Command/User/Add.php index 033d2bdc9a2..4de4e247991 100644 --- a/core/Command/User/Add.php +++ b/core/Command/User/Add.php @@ -52,7 +52,7 @@ class Add extends Command { 'password-from-env', null, InputOption::VALUE_NONE, - 'read password from environment variable OC_PASS' + 'read password from environment variable NC_PASS/OC_PASS' ) ->addOption( 'generate-password', @@ -91,10 +91,10 @@ class Add extends Command { // Setup password. if ($input->getOption('password-from-env')) { - $password = getenv('OC_PASS'); + $password = getenv('NC_PASS') ?: getenv('OC_PASS'); if (!$password) { - $output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>'); + $output->writeln('<error>--password-from-env given, but NC_PASS/OC_PASS is empty!</error>'); return 1; } } elseif ($input->getOption('generate-password')) { diff --git a/core/Command/User/AuthTokens/Add.php b/core/Command/User/AuthTokens/Add.php index ad4bf732bd0..89b20535c63 100644 --- a/core/Command/User/AuthTokens/Add.php +++ b/core/Command/User/AuthTokens/Add.php @@ -62,9 +62,9 @@ class Add extends Command { } if ($input->getOption('password-from-env')) { - $password = getenv('NC_PASS') ?? getenv('OC_PASS'); + $password = getenv('NC_PASS') ?: getenv('OC_PASS'); if (!$password) { - $output->writeln('<error>--password-from-env given, but NC_PASS is empty!</error>'); + $output->writeln('<error>--password-from-env given, but NC_PASS/OC_PASS is empty!</error>'); return 1; } } elseif ($input->isInteractive()) { diff --git a/core/Command/User/AuthTokens/Delete.php b/core/Command/User/AuthTokens/Delete.php index f2c75a8ad99..2047d2eae2a 100644 --- a/core/Command/User/AuthTokens/Delete.php +++ b/core/Command/User/AuthTokens/Delete.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/core/Command/User/AuthTokens/ListCommand.php b/core/Command/User/AuthTokens/ListCommand.php index 1ebd4a0f0b4..b36aa717505 100644 --- a/core/Command/User/AuthTokens/ListCommand.php +++ b/core/Command/User/AuthTokens/ListCommand.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/core/Command/User/Info.php b/core/Command/User/Info.php index 6487b533fa2..e7fc9286e74 100644 --- a/core/Command/User/Info.php +++ b/core/Command/User/Info.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later @@ -6,6 +7,7 @@ namespace OC\Core\Command\User; use OC\Core\Command\Base; +use OCP\Files\NotFoundException; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; @@ -84,7 +86,7 @@ class Info extends Base { \OC_Util::setupFS($user->getUID()); try { $storage = \OC_Helper::getStorageInfo('/'); - } catch (\OCP\Files\NotFoundException $e) { + } catch (NotFoundException $e) { return []; } return [ diff --git a/core/Command/User/LastSeen.php b/core/Command/User/LastSeen.php index dbb611a4fff..984def72cd6 100644 --- a/core/Command/User/LastSeen.php +++ b/core/Command/User/LastSeen.php @@ -68,7 +68,7 @@ class LastSeen extends Base { return 1; } - $this->userManager->callForAllUsers(static function (IUser $user) use ($output) { + $this->userManager->callForAllUsers(static function (IUser $user) use ($output): void { $lastLogin = $user->getLastLogin(); if ($lastLogin === 0) { $output->writeln($user->getUID() . ' has never logged in.'); diff --git a/core/Command/User/ListCommand.php b/core/Command/User/ListCommand.php index e7fb3de71f0..66b831c793b 100644 --- a/core/Command/User/ListCommand.php +++ b/core/Command/User/ListCommand.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/core/Command/User/Profile.php b/core/Command/User/Profile.php new file mode 100644 index 00000000000..fd5fbed08cd --- /dev/null +++ b/core/Command/User/Profile.php @@ -0,0 +1,234 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Core\Command\User; + +use OC\Core\Command\Base; +use OCP\Accounts\IAccount; +use OCP\Accounts\IAccountManager; +use OCP\Accounts\PropertyDoesNotExistException; +use OCP\IUser; +use OCP\IUserManager; +use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class Profile extends Base { + public function __construct( + protected IUserManager $userManager, + protected IAccountManager $accountManager, + ) { + parent::__construct(); + } + + protected function configure() { + parent::configure(); + $this + ->setName('user:profile') + ->setDescription('Read and modify user profile properties') + ->addArgument( + 'uid', + InputArgument::REQUIRED, + 'Account ID used to login' + ) + ->addArgument( + 'key', + InputArgument::OPTIONAL, + 'Profile property to set, get or delete', + '' + ) + + // Get + ->addOption( + 'default-value', + null, + InputOption::VALUE_REQUIRED, + '(Only applicable on get) If no default value is set and the property does not exist, the command will exit with 1' + ) + + // Set + ->addArgument( + 'value', + InputArgument::OPTIONAL, + 'The new value of the property', + null + ) + ->addOption( + 'update-only', + null, + InputOption::VALUE_NONE, + 'Only updates the value, if it is not set before, it is not being added' + ) + + // Delete + ->addOption( + 'delete', + null, + InputOption::VALUE_NONE, + 'Specify this option to delete the property value' + ) + ->addOption( + 'error-if-not-exists', + null, + InputOption::VALUE_NONE, + 'Checks whether the property exists before deleting it' + ) + ; + } + + protected function checkInput(InputInterface $input): IUser { + $uid = $input->getArgument('uid'); + $user = $this->userManager->get($uid); + if (!$user) { + throw new \InvalidArgumentException('The user "' . $uid . '" does not exist.'); + } + // normalize uid + $input->setArgument('uid', $user->getUID()); + + $key = $input->getArgument('key'); + if ($key === '') { + if ($input->hasParameterOption('--default-value')) { + throw new \InvalidArgumentException('The "default-value" option can only be used when specifying a key.'); + } + if ($input->getArgument('value') !== null) { + throw new \InvalidArgumentException('The value argument can only be used when specifying a key.'); + } + if ($input->getOption('delete')) { + throw new \InvalidArgumentException('The "delete" option can only be used when specifying a key.'); + } + } + + if ($input->getArgument('value') !== null && $input->hasParameterOption('--default-value')) { + throw new \InvalidArgumentException('The value argument can not be used together with "default-value".'); + } + if ($input->getOption('update-only') && $input->getArgument('value') === null) { + throw new \InvalidArgumentException('The "update-only" option can only be used together with "value".'); + } + + if ($input->getOption('delete') && $input->hasParameterOption('--default-value')) { + throw new \InvalidArgumentException('The "delete" option can not be used together with "default-value".'); + } + if ($input->getOption('delete') && $input->getArgument('value') !== null) { + throw new \InvalidArgumentException('The "delete" option can not be used together with "value".'); + } + if ($input->getOption('error-if-not-exists') && !$input->getOption('delete')) { + throw new \InvalidArgumentException('The "error-if-not-exists" option can only be used together with "delete".'); + } + + return $user; + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + try { + $user = $this->checkInput($input); + } catch (\InvalidArgumentException $e) { + $output->writeln('<error>' . $e->getMessage() . '</error>'); + return self::FAILURE; + } + + $uid = $input->getArgument('uid'); + $key = $input->getArgument('key'); + $userAccount = $this->accountManager->getAccount($user); + + if ($key === '') { + $settings = $this->getAllProfileProperties($userAccount); + $this->writeArrayInOutputFormat($input, $output, $settings); + return self::SUCCESS; + } + + $value = $this->getStoredValue($userAccount, $key); + $inputValue = $input->getArgument('value'); + if ($inputValue !== null) { + if ($input->hasParameterOption('--update-only') && $value === null) { + $output->writeln('<error>The property does not exist for user "' . $uid . '".</error>'); + return self::FAILURE; + } + + return $this->editProfileProperty($output, $userAccount, $key, $inputValue); + } elseif ($input->hasParameterOption('--delete')) { + if ($input->hasParameterOption('--error-if-not-exists') && $value === null) { + $output->writeln('<error>The property does not exist for user "' . $uid . '".</error>'); + return self::FAILURE; + } + + return $this->deleteProfileProperty($output, $userAccount, $key); + } elseif ($value !== null) { + $output->writeln($value); + } elseif ($input->hasParameterOption('--default-value')) { + $output->writeln($input->getOption('default-value')); + } else { + $output->writeln('<error>The property does not exist for user "' . $uid . '".</error>'); + return self::FAILURE; + } + + return self::SUCCESS; + } + + private function deleteProfileProperty(OutputInterface $output, IAccount $userAccount, string $key): int { + return $this->editProfileProperty($output, $userAccount, $key, ''); + } + + private function editProfileProperty(OutputInterface $output, IAccount $userAccount, string $key, string $value): int { + try { + $userAccount->getProperty($key)->setValue($value); + } catch (PropertyDoesNotExistException $exception) { + $output->writeln('<error>' . $exception->getMessage() . '</error>'); + return self::FAILURE; + } + + $this->accountManager->updateAccount($userAccount); + return self::SUCCESS; + } + + private function getStoredValue(IAccount $userAccount, string $key): ?string { + try { + $property = $userAccount->getProperty($key); + } catch (PropertyDoesNotExistException) { + return null; + } + return $property->getValue() === '' ? null : $property->getValue(); + } + + private function getAllProfileProperties(IAccount $userAccount): array { + $properties = []; + + foreach ($userAccount->getAllProperties() as $property) { + if ($property->getValue() !== '') { + $properties[$property->getName()] = $property->getValue(); + } + } + + return $properties; + } + + /** + * @param string $argumentName + * @param CompletionContext $context + * @return string[] + */ + public function completeArgumentValues($argumentName, CompletionContext $context): array { + if ($argumentName === 'uid') { + return array_map(static fn (IUser $user) => $user->getUID(), $this->userManager->search($context->getCurrentWord())); + } + if ($argumentName === 'key') { + $userId = $context->getWordAtIndex($context->getWordIndex() - 1); + $user = $this->userManager->get($userId); + if (!($user instanceof IUser)) { + return []; + } + + $account = $this->accountManager->getAccount($user); + + $properties = $this->getAllProfileProperties($account); + return array_keys($properties); + } + return []; + } +} diff --git a/core/Command/User/ResetPassword.php b/core/Command/User/ResetPassword.php index 2f18c3d473e..0e8b1325770 100644 --- a/core/Command/User/ResetPassword.php +++ b/core/Command/User/ResetPassword.php @@ -41,7 +41,7 @@ class ResetPassword extends Base { 'password-from-env', null, InputOption::VALUE_NONE, - 'read password from environment variable OC_PASS' + 'read password from environment variable NC_PASS/OC_PASS' ) ; } @@ -56,9 +56,9 @@ class ResetPassword extends Base { } if ($input->getOption('password-from-env')) { - $password = getenv('OC_PASS'); + $password = getenv('NC_PASS') ?: getenv('OC_PASS'); if (!$password) { - $output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>'); + $output->writeln('<error>--password-from-env given, but NC_PASS/OC_PASS is empty!</error>'); return 1; } } elseif ($input->isInteractive()) { diff --git a/core/Command/User/SyncAccountDataCommand.php b/core/Command/User/SyncAccountDataCommand.php index 640b66581e1..c353df6fe9f 100644 --- a/core/Command/User/SyncAccountDataCommand.php +++ b/core/Command/User/SyncAccountDataCommand.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later @@ -16,15 +17,10 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class SyncAccountDataCommand extends Base { - protected IUserManager $userManager; - protected IAccountManager $accountManager; - public function __construct( - IUserManager $userManager, - IAccountManager $accountManager, + protected IUserManager $userManager, + protected IAccountManager $accountManager, ) { - $this->userManager = $userManager; - $this->accountManager = $accountManager; parent::__construct(); } diff --git a/core/Command/User/Welcome.php b/core/Command/User/Welcome.php index c383811f982..65637759689 100644 --- a/core/Command/User/Welcome.php +++ b/core/Command/User/Welcome.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2023 FedericoHeichou <federicoheichou@gmail.com> * SPDX-License-Identifier: AGPL-3.0-or-later @@ -15,24 +16,15 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class Welcome extends Base { - /** @var IUserManager */ - protected $userManager; - - /** @var NewUserMailHelper */ - private $newUserMailHelper; - /** * @param IUserManager $userManager * @param NewUserMailHelper $newUserMailHelper */ public function __construct( - IUserManager $userManager, - NewUserMailHelper $newUserMailHelper, + protected IUserManager $userManager, + private NewUserMailHelper $newUserMailHelper, ) { parent::__construct(); - - $this->userManager = $userManager; - $this->newUserMailHelper = $newUserMailHelper; } /** |