diff --git a/app/modules/cli/Commands/Crypt/UpdateMasterPasswordCommand.php b/app/modules/cli/Commands/Crypt/UpdateMasterPasswordCommand.php index 3647f72f..90fdb425 100644 --- a/app/modules/cli/Commands/Crypt/UpdateMasterPasswordCommand.php +++ b/app/modules/cli/Commands/Crypt/UpdateMasterPasswordCommand.php @@ -120,9 +120,21 @@ final class UpdateMasterPasswordCommand extends CommandBase $this->checkInstalled(); $this->checkMaintenance(); + $masterPassword = $this->getMasterPassword($input, $style); + $currentMasterPassword = $this->getCurrentMasterPassword($input, $style); + + if ($masterPassword === $currentMasterPassword) { + $this->logger->debug(__u('Passwords are the same')); + $style->info(__('Passwords are the same')); + + return self::FAILURE; + } + + $this->checkMasterPassword($currentMasterPassword); + $request = new UpdateMasterPassRequest( - $this->getCurrentMasterPassword($input, $style), - $this->getMasterPassword($input, $style), + $currentMasterPassword, + $masterPassword, $this->configService->getByParam(MasterPassService::PARAM_MASTER_PASS_HASH) ); @@ -177,23 +189,41 @@ final class UpdateMasterPasswordCommand extends CommandBase * @param InputInterface $input * @param StyleInterface $style * - * @return bool + * @return array|false|mixed|string */ - private function getUpdate(InputInterface $input, StyleInterface $style): bool + private function getMasterPassword( + InputInterface $input, + StyleInterface $style + ) { - $option = 'update'; + $password = self::getEnvVarOrOption('masterPassword', $input); - $envUpdate = self::getEnvVarForOption($option); + if (empty($password)) { + $this->logger->debug(__u('Ask for master password')); - $value = $envUpdate !== false - ? Util::boolval($envUpdate) - : $input->getOption($option); + $password = $style->askHidden( + __('Please provide the new master password'), + fn($value) => Validators::valueNotEmpty( + $value, + sprintf(__u('%s cannot be blank'), 'Master password') + ) + ); + $passwordRepeat = $style->askHidden( + __('Please provide the new master password again'), + fn($value) => Validators::valueNotEmpty( + $value, + sprintf(__u('%s cannot be blank'), 'Master password') + ) + ); - if ($value === false) { - return $style->confirm(__('Update master password? (This process cannot be undone)'), false); + if ($password !== $passwordRepeat) { + throw new RuntimeException(__u('Passwords do not match')); + } elseif (null === $password || null === $passwordRepeat) { + throw new RuntimeException(sprintf(__u('%s cannot be blank'), 'Master password')); + } } - return true; + return $password; } /** @@ -237,44 +267,37 @@ final class UpdateMasterPasswordCommand extends CommandBase return $password; } + /** + * @throws \SP\Services\ServiceException + * @throws \SP\Repositories\NoSuchItemException + */ + private function checkMasterPassword(string $password): void + { + if (!$this->masterPassService->checkMasterPassword($password)) { + throw new RuntimeException(__u('The current master password does not match')); + } + } + /** * @param InputInterface $input * @param StyleInterface $style * - * @return array|false|mixed|string + * @return bool */ - private function getMasterPassword( - InputInterface $input, - StyleInterface $style - ) + private function getUpdate(InputInterface $input, StyleInterface $style): bool { - $password = self::getEnvVarOrOption('masterPassword', $input); + $option = 'update'; - if (empty($password)) { - $this->logger->debug(__u('Ask for master password')); + $envUpdate = self::getEnvVarForOption($option); - $password = $style->askHidden( - __('Please provide the new master password'), - fn($value) => Validators::valueNotEmpty( - $value, - sprintf(__u('%s cannot be blank'), 'Master password') - ) - ); - $passwordRepeat = $style->askHidden( - __('Please provide the new master password again'), - fn($value) => Validators::valueNotEmpty( - $value, - sprintf(__u('%s cannot be blank'), 'Master password') - ) - ); + $value = $envUpdate !== false + ? Util::boolval($envUpdate) + : $input->getOption($option); - if ($password !== $passwordRepeat) { - throw new RuntimeException(__u('Passwords do not match')); - } elseif (null === $password || null === $passwordRepeat) { - throw new RuntimeException(sprintf(__u('%s cannot be blank'), 'Master password')); - } + if ($value === false) { + return $style->confirm(__('Update master password? (This process cannot be undone)'), false); } - return $password; + return true; } } \ No newline at end of file diff --git a/tests/SP/Modules/Cli/Commands/BackupCommandTest.php b/tests/SP/Modules/Cli/Commands/BackupCommandTest.php index 37d19a9a..b2b9fdc8 100644 --- a/tests/SP/Modules/Cli/Commands/BackupCommandTest.php +++ b/tests/SP/Modules/Cli/Commands/BackupCommandTest.php @@ -165,4 +165,18 @@ class BackupCommandTest extends CliTestCase TMP_PATH) ); } + + protected function setUp(): void + { + $this->unsetEnvironmentVariables(); + + parent::setUp(); + } + + private function unsetEnvironmentVariables(): void + { + foreach (BackupCommand::$envVarsMapping as $envVar) { + putenv($envVar); + } + } } diff --git a/tests/SP/Modules/Cli/Commands/InstallCommandTest.php b/tests/SP/Modules/Cli/Commands/InstallCommandTest.php index 7fc4293d..411dcdc9 100644 --- a/tests/SP/Modules/Cli/Commands/InstallCommandTest.php +++ b/tests/SP/Modules/Cli/Commands/InstallCommandTest.php @@ -380,4 +380,18 @@ class InstallCommandTest extends CliTestCase // Cleanup database DatabaseUtil::dropDatabase(self::$commandInputData['databaseName']); } + + protected function setUp(): void + { + $this->unsetEnvironmentVariables(); + + parent::setUp(); + } + + private function unsetEnvironmentVariables(): void + { + foreach (InstallCommand::$envVarsMapping as $envVar) { + putenv($envVar); + } + } } diff --git a/tests/SP/Modules/Cli/Commands/UpdateMasterPasswordCommandTest.php b/tests/SP/Modules/Cli/Commands/UpdateMasterPasswordCommandTest.php index 3b513395..52c5976c 100644 --- a/tests/SP/Modules/Cli/Commands/UpdateMasterPasswordCommandTest.php +++ b/tests/SP/Modules/Cli/Commands/UpdateMasterPasswordCommandTest.php @@ -114,6 +114,18 @@ class UpdateMasterPasswordCommandTest extends CliTestCase $this->assertStringContainsString('Master password update aborted', $output); } + private function setEnvironmentVariables(): void + { + putenv(sprintf('%s=%s', + UpdateMasterPasswordCommand::$envVarsMapping['currentMasterPassword'], + AccountCryptServiceTest::CURRENT_MASTERPASS) + ); + putenv(sprintf('%s=%s', + UpdateMasterPasswordCommand::$envVarsMapping['masterPassword'], + AccountCryptServiceTest::NEW_MASTERPASS) + ); + } + /** * @throws DependencyException * @throws NotFoundException @@ -156,18 +168,6 @@ class UpdateMasterPasswordCommandTest extends CliTestCase $this->assertStringContainsString('Master password cannot be blank', $output); } - private function setEnvironmentVariables(): void - { - putenv(sprintf('%s=%s', - UpdateMasterPasswordCommand::$envVarsMapping['currentMasterPassword'], - AccountCryptServiceTest::CURRENT_MASTERPASS) - ); - putenv(sprintf('%s=%s', - UpdateMasterPasswordCommand::$envVarsMapping['masterPassword'], - AccountCryptServiceTest::NEW_MASTERPASS) - ); - } - /** * @throws DependencyException * @throws NotFoundException @@ -191,12 +191,65 @@ class UpdateMasterPasswordCommandTest extends CliTestCase $this->assertStringContainsString('Master password updated', $output); } + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + */ + public function testSameMasterPassword(): void + { + $inputData = [ + '--currentMasterPassword' => AccountCryptServiceTest::CURRENT_MASTERPASS, + '--masterPassword' => AccountCryptServiceTest::CURRENT_MASTERPASS, + '--update' => null + ]; + + $commandTester = $this->executeCommandTest( + UpdateMasterPasswordCommand::class, + $inputData + ); + + // the output of the command in the console + $output = $commandTester->getDisplay(); + $this->assertStringContainsString('Passwords are the same', $output); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + */ + public function testWrongMasterPassword(): void + { + $inputData = [ + '--currentMasterPassword' => uniqid('', true), + '--masterPassword' => AccountCryptServiceTest::NEW_MASTERPASS, + '--update' => null + ]; + + $commandTester = $this->executeCommandTest( + UpdateMasterPasswordCommand::class, + $inputData + ); + + // the output of the command in the console + $output = $commandTester->getDisplay(); + $this->assertStringContainsString('The current master password does not match', $output); + } + protected function setUp(): void { + $this->unsetEnvironmentVariables(); + $this->setupDatabase(); self::loadFixtures(); parent::setUp(); } + + private function unsetEnvironmentVariables(): void + { + foreach (UpdateMasterPasswordCommand::$envVarsMapping as $envVar) { + putenv($envVar); + } + } }