From a61abc51c3c186e1cb243c176e9b640e7fb0f0be Mon Sep 17 00:00:00 2001 From: stellarpower Date: Tue, 19 Mar 2024 16:52:34 +0000 Subject: [PATCH] Add occ command for checking the password for a given user ID. --- core/Command/User/CheckPassword.php | 153 ++++++++++++++++++++ core/register_command.php | 1 + lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + 4 files changed, 156 insertions(+) create mode 100644 core/Command/User/CheckPassword.php diff --git a/core/Command/User/CheckPassword.php b/core/Command/User/CheckPassword.php new file mode 100644 index 0000000000000..7c14ef0785e16 --- /dev/null +++ b/core/Command/User/CheckPassword.php @@ -0,0 +1,153 @@ + + * @author Christopher Schäpers + * @author Clark Tomlinson + * @author Joas Schilling + * @author Laurens Post + * @author Roeland Jago Douma + * @author Sujith H + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ +namespace OC\Core\Command\User; + +use OC\Core\Command\Base; +use OCP\App\IAppManager; +use OCP\IUser; +use OCP\IUserManager; +use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +class CheckPassword extends Base { + public function __construct( + protected IUserManager $userManager, + private IAppManager $appManager, + ) { + parent::__construct(); + } + + protected function configure() { + $this + ->setName('user:checkpassword') + ->setDescription('Look up the user by ID and check the given password') + ->addArgument( + 'user', + InputArgument::REQUIRED, + 'User ID' + ) + ->addArgument( + 'password', + InputArgument::OPTIONAL, + 'User\'s password - if not provided, will be prompted interactively if a TTY is available or read from STDIN' + ) + ->addOption( + 'password-from-env', + null, + InputOption::VALUE_NONE, + 'read password from environment variable OC_PASS' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $userID = $input->getArgument('user'); + + $user = $this->userManager->get($userID); + + if (is_null($user)) { + $output->writeln('User does not exist'); + return 1; + } + + // Get from the environment variable + if ($input->getOption('password-from-env')) { + $password = getenv('OC_PASS'); + if (!$password) { + $output->writeln('--password-from-env given, but OC_PASS is empty!'); + return 2; + } + } else { + // Was an argument provided? + $password = $input->getArgument('password'); + + if (is_null($password)) { + // It was not. + + //If interactive, we prompt + if ($input->isInteractive()) { + /** @var QuestionHelper $helper */ + $helper = $this->getHelper('question'); + + $question = new Question('Enter user\'s password: '); + $question->setHidden(true); + $password = $helper->ask($input, $output, $question); + + if ($password === null) { + $output->writeln("Password cannot be empty!"); + return 2; + } + } else { + // FIXME: + // isInteractive seems to return true even when data are piped in + // We want to detect if we are in a pipeline and not prompt for the question + + // Else, we try readin from stdin if the user piped it + $password = fgets(STDIN); + if ($password === false) { + // STDIN was closed. + // No password provided, and not interactive, so we can't do anything. + $output->writeln('--Password not given but TTY is not interactive and no data on STDIN!'); + return 2; + } + + } + } + + } // argument not provided + + if ($this->userManager->checkPassword($userID, $password)) { + $output->writeln('Password okay'); + return 0; + } else { + $output->writeln('Password is incorrect'); + return 1; + } + + + } + + + /** + * @param string $argumentName + * @param CompletionContext $context + * @return string[] + */ + public function completeArgumentValues($argumentName, CompletionContext $context) { + if ($argumentName === 'user') { + return array_map(static fn (IUser $user) => $user->getUID(), $this->userManager->search($context->getCurrentWord())); + } + return []; + } +} diff --git a/core/register_command.php b/core/register_command.php index 4a84e551ce0ce..bad7bdd435537 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -148,6 +148,7 @@ $application->add(Server::get(Command\Preview\ResetRenderedTexts::class)); $application->add(Server::get(Command\User\Add::class)); + $application->add(Server::get(Command\User\CheckPassword::class)); // Tested with $application->add(new OC\Core\Command\User\CheckPassword(\OC::$server->getUserManager())); $application->add(Server::get(Command\User\Delete::class)); $application->add(Server::get(Command\User\Disable::class)); $application->add(Server::get(Command\User\Enable::class)); diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 3aa5c6a2b8cf9..68a1c4e7abbb2 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1133,6 +1133,7 @@ 'OC\\Core\\Command\\User\\AuthTokens\\Add' => $baseDir . '/core/Command/User/AuthTokens/Add.php', 'OC\\Core\\Command\\User\\AuthTokens\\Delete' => $baseDir . '/core/Command/User/AuthTokens/Delete.php', 'OC\\Core\\Command\\User\\AuthTokens\\ListCommand' => $baseDir . '/core/Command/User/AuthTokens/ListCommand.php', + 'OC\\Core\\Command\\User\\CheckPassword' => $baseDir . '/core/Command/User/CheckPassword.php', 'OC\\Core\\Command\\User\\Delete' => $baseDir . '/core/Command/User/Delete.php', 'OC\\Core\\Command\\User\\Disable' => $baseDir . '/core/Command/User/Disable.php', 'OC\\Core\\Command\\User\\Enable' => $baseDir . '/core/Command/User/Enable.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 21d6778432210..28dd80e817260 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1166,6 +1166,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Command\\User\\AuthTokens\\Add' => __DIR__ . '/../../..' . '/core/Command/User/AuthTokens/Add.php', 'OC\\Core\\Command\\User\\AuthTokens\\Delete' => __DIR__ . '/../../..' . '/core/Command/User/AuthTokens/Delete.php', 'OC\\Core\\Command\\User\\AuthTokens\\ListCommand' => __DIR__ . '/../../..' . '/core/Command/User/AuthTokens/ListCommand.php', + 'OC\\Core\\Command\\User\\CheckPassword' => __DIR__ . '/../../..' . '/core/Command/User/CheckPassword.php', 'OC\\Core\\Command\\User\\Delete' => __DIR__ . '/../../..' . '/core/Command/User/Delete.php', 'OC\\Core\\Command\\User\\Disable' => __DIR__ . '/../../..' . '/core/Command/User/Disable.php', 'OC\\Core\\Command\\User\\Enable' => __DIR__ . '/../../..' . '/core/Command/User/Enable.php',