diff --git a/libraries/src/Console/AddUserCommand.php b/libraries/src/Console/AddUserCommand.php index 93c944641852a..6b61c6d5f8539 100644 --- a/libraries/src/Console/AddUserCommand.php +++ b/libraries/src/Console/AddUserCommand.php @@ -9,6 +9,8 @@ namespace Joomla\CMS\Console; +use Joomla\CMS\Form\Form; +use Joomla\CMS\Form\Rule\PasswordRule; use Joomla\CMS\User\User; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; @@ -128,12 +130,15 @@ public function __construct(DatabaseInterface $db) */ protected function doExecute(InputInterface $input, OutputInterface $output): int { + $language = $this->getApplication()->getLanguage(); + $language->load('lib_joomla', JPATH_ADMINISTRATOR, 'en-GB', true); $this->configureIO($input, $output); $this->ioStyle->title('Add User'); + $this->user = $this->getStringFromOption('username', 'Please enter a username'); $this->name = $this->getStringFromOption('name', 'Please enter a name (full name of user)'); $this->email = $this->getStringFromOption('email', 'Please enter an email address'); - $this->password = $this->getStringFromOption('password', 'Please enter a password'); + $this->password = $this->getPasswordFromOption(); $this->userGroups = $this->getUserGroups(); if (\in_array("error", $this->userGroups)) { @@ -142,29 +147,51 @@ protected function doExecute(InputInterface $input, OutputInterface $output): in return Command::FAILURE; } - // Get filter to remove invalid characters $filter = new InputFilter(); - $user = [ - 'username' => $filter->clean($this->user, 'USERNAME'), - 'password' => $this->password, - 'name' => $filter->clean($this->name, 'STRING'), - 'email' => $this->email, - 'groups' => $this->userGroups, + $data = [ + 'username' => $filter->clean($this->user, 'USERNAME'), + 'password' => $this->password, + 'password2' => $this->password, + 'name' => $filter->clean($this->name, 'STRING'), + 'email' => $this->email, + 'groups' => $this->userGroups, ]; + $form = new Form('com_users.user', ['control' => 'jform']); + $xmlPath = JPATH_ADMINISTRATOR . '/components/com_users/forms/user.xml'; + + if (!is_file($xmlPath) || !$form->loadFile($xmlPath)) { + $this->ioStyle->error('Unable to load user form XML: ' . $xmlPath); + + return Command::FAILURE; + } + + $validated = $form->validate($data); + $this->clearMessageQueue(); + + if ($validated === false) { + $errors = $form->getErrors(); + + foreach ($errors as $error) { + $this->ioStyle->error($error->getMessage()); + } + + return Command::FAILURE; + } + $userObj = User::getInstance(); - $userObj->bind($user); + $userObj->bind($data); if (!$userObj->save()) { switch ($userObj->getError()) { - case "JLIB_DATABASE_ERROR_USERNAME_INUSE": + case "Username in use.": $this->ioStyle->error("The username already exists!"); break; - case "JLIB_DATABASE_ERROR_EMAIL_INUSE": + case "The email address you entered is already in use. Please enter another email address.": $this->ioStyle->error("The email address already exists!"); break; - case "JLIB_DATABASE_ERROR_VALID_MAIL": + case "The email address you entered is invalid. Please enter another email address.": $this->ioStyle->error("The email address is invalid!"); break; } @@ -172,6 +199,7 @@ protected function doExecute(InputInterface $input, OutputInterface $output): in return 1; } + $this->clearMessageQueue(); $this->ioStyle->success("User created!"); return Command::SUCCESS; @@ -189,7 +217,7 @@ protected function doExecute(InputInterface $input, OutputInterface $output): in protected function getGroupId($groupName) { $db = $this->getDatabase(); - $query = $db->getQuery(true) + $query = $db->createQuery() ->select($db->quoteName('id')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('title') . ' = :groupName') @@ -224,6 +252,37 @@ public function getStringFromOption($option, $question): string return $answer; } + /** + * Method to get and validate password from option + * + * @return string + * + * @since __DEPLOY_VERSION__ + */ + public function getPasswordFromOption(): string + { + $password = (string) $this->cliInput->getOption('password'); + + while (!$password) { + $password = (string) $this->ioStyle->askHidden('Please enter a password'); + + if ($password) { + $errors = $this->validatePassword($password); + + if ($errors) { + foreach ($errors as $error) { + $this->ioStyle->error($error); + } + $password = ''; + } + } + } + + $this->clearMessageQueue(); + + return $password; + } + /** * Method to get a value from option * @@ -257,7 +316,7 @@ protected function getUserGroups(): array } // Generate select list for user - $query = $db->getQuery(true) + $query = $db->createQuery() ->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->order($db->quoteName('id') . 'ASC'); @@ -316,4 +375,53 @@ protected function configure(): void $this->setDescription('Add a user'); $this->setHelp($help); } + + /** + * Method to validate password using Joomla's PasswordRule + * + * @param string $password The password to validate + * + * @return array Array of error messages, empty if valid + * + * @since __DEPLOY_VERSION__ + */ + protected function validatePassword($password): array + { + $errors = []; + + // Construct the XML field element matching password rules + $element = new \SimpleXMLElement(''); + + // Instantiate and execute the rule + $rule = new PasswordRule(); + $isValid = $rule->test($element, $password); + + $app = $this->getApplication(); + + + $messageQueue = $app->getMessageQueue(true); + + if (!$isValid) { + foreach ($messageQueue as $message) { + $errors[] = $message; + } + } + + return $errors; + } + + /** + * Clears the application's message queue. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + private function clearMessageQueue(): void + { + $app = $this->getApplication(); + + $property = new \ReflectionProperty($app, 'messages'); + $property->setValue($app, []); + } }