Add.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Anupam Kumar <kyteinsky@gmail.com>
  6. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  7. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  8. * @author Joas Schilling <coding@schilljs.com>
  9. * @author Laurens Post <lkpost@scept.re>
  10. * @author Roeland Jago Douma <roeland@famdouma.nl>
  11. *
  12. * @license AGPL-3.0
  13. *
  14. * This code is free software: you can redistribute it and/or modify
  15. * it under the terms of the GNU Affero General Public License, version 3,
  16. * as published by the Free Software Foundation.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU Affero General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU Affero General Public License, version 3,
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>
  25. *
  26. */
  27. namespace OC\Core\Command\User;
  28. use OC\Files\Filesystem;
  29. use OCA\Settings\Mailer\NewUserMailHelper;
  30. use OCP\EventDispatcher\IEventDispatcher;
  31. use OCP\IAppConfig;
  32. use OCP\IGroup;
  33. use OCP\IGroupManager;
  34. use OCP\IUser;
  35. use OCP\IUserManager;
  36. use OCP\Mail\IMailer;
  37. use OCP\Security\Events\GenerateSecurePasswordEvent;
  38. use OCP\Security\ISecureRandom;
  39. use Symfony\Component\Console\Command\Command;
  40. use Symfony\Component\Console\Helper\QuestionHelper;
  41. use Symfony\Component\Console\Input\InputArgument;
  42. use Symfony\Component\Console\Input\InputInterface;
  43. use Symfony\Component\Console\Input\InputOption;
  44. use Symfony\Component\Console\Output\OutputInterface;
  45. use Symfony\Component\Console\Question\Question;
  46. class Add extends Command {
  47. public function __construct(
  48. protected IUserManager $userManager,
  49. protected IGroupManager $groupManager,
  50. protected IMailer $mailer,
  51. private IAppConfig $appConfig,
  52. private NewUserMailHelper $mailHelper,
  53. private IEventDispatcher $eventDispatcher,
  54. private ISecureRandom $secureRandom,
  55. ) {
  56. parent::__construct();
  57. }
  58. protected function configure(): void {
  59. $this
  60. ->setName('user:add')
  61. ->setDescription('adds an account')
  62. ->addArgument(
  63. 'uid',
  64. InputArgument::REQUIRED,
  65. 'Account ID used to login (must only contain a-z, A-Z, 0-9, -, _ and @)'
  66. )
  67. ->addOption(
  68. 'password-from-env',
  69. null,
  70. InputOption::VALUE_NONE,
  71. 'read password from environment variable OC_PASS'
  72. )
  73. ->addOption(
  74. 'generate-password',
  75. null,
  76. InputOption::VALUE_NONE,
  77. 'Generate a secure password. A welcome email with a reset link will be sent to the user via an email if --email option and newUser.sendEmail config are set'
  78. )
  79. ->addOption(
  80. 'display-name',
  81. null,
  82. InputOption::VALUE_OPTIONAL,
  83. 'Login used in the web UI (can contain any characters)'
  84. )
  85. ->addOption(
  86. 'group',
  87. 'g',
  88. InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
  89. 'groups the account should be added to (The group will be created if it does not exist)'
  90. )
  91. ->addOption(
  92. 'email',
  93. null,
  94. InputOption::VALUE_REQUIRED,
  95. 'When set, users may register using the default email verification workflow'
  96. );
  97. }
  98. protected function execute(InputInterface $input, OutputInterface $output): int {
  99. $uid = $input->getArgument('uid');
  100. if ($this->userManager->userExists($uid)) {
  101. $output->writeln('<error>The account "' . $uid . '" already exists.</error>');
  102. return 1;
  103. }
  104. $password = '';
  105. // Setup password.
  106. if ($input->getOption('password-from-env')) {
  107. $password = getenv('OC_PASS');
  108. if (!$password) {
  109. $output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>');
  110. return 1;
  111. }
  112. } elseif ($input->getOption('generate-password')) {
  113. $passwordEvent = new GenerateSecurePasswordEvent();
  114. $this->eventDispatcher->dispatchTyped($passwordEvent);
  115. $password = $passwordEvent->getPassword() ?? $this->secureRandom->generate(20);
  116. } elseif ($input->isInteractive()) {
  117. /** @var QuestionHelper $helper */
  118. $helper = $this->getHelper('question');
  119. $question = new Question('Enter password: ');
  120. $question->setHidden(true);
  121. $password = $helper->ask($input, $output, $question);
  122. $question = new Question('Confirm password: ');
  123. $question->setHidden(true);
  124. $confirm = $helper->ask($input, $output, $question);
  125. if ($password !== $confirm) {
  126. $output->writeln("<error>Passwords did not match!</error>");
  127. return 1;
  128. }
  129. } else {
  130. $output->writeln("<error>Interactive input or --password-from-env or --generate-password is needed for setting a password!</error>");
  131. return 1;
  132. }
  133. try {
  134. $user = $this->userManager->createUser(
  135. $input->getArgument('uid'),
  136. $password,
  137. );
  138. } catch (\Exception $e) {
  139. $output->writeln('<error>' . $e->getMessage() . '</error>');
  140. return 1;
  141. }
  142. if ($user instanceof IUser) {
  143. $output->writeln('<info>The account "' . $user->getUID() . '" was created successfully</info>');
  144. } else {
  145. $output->writeln('<error>An error occurred while creating the account</error>');
  146. return 1;
  147. }
  148. if ($input->getOption('display-name')) {
  149. $user->setDisplayName($input->getOption('display-name'));
  150. $output->writeln('Display name set to "' . $user->getDisplayName() . '"');
  151. }
  152. $groups = $input->getOption('group');
  153. if (!empty($groups)) {
  154. // Make sure we init the Filesystem for the user, in case we need to
  155. // init some group shares.
  156. Filesystem::init($user->getUID(), '');
  157. }
  158. foreach ($groups as $groupName) {
  159. $group = $this->groupManager->get($groupName);
  160. if (!$group) {
  161. $this->groupManager->createGroup($groupName);
  162. $group = $this->groupManager->get($groupName);
  163. if ($group instanceof IGroup) {
  164. $output->writeln('Created group "' . $group->getGID() . '"');
  165. }
  166. }
  167. if ($group instanceof IGroup) {
  168. $group->addUser($user);
  169. $output->writeln('Account "' . $user->getUID() . '" added to group "' . $group->getGID() . '"');
  170. }
  171. }
  172. $email = $input->getOption('email');
  173. if (!empty($email)) {
  174. if (!$this->mailer->validateMailAddress($email)) {
  175. $output->writeln(\sprintf(
  176. '<error>The given email address "%s" is invalid. Email not set for the user.</error>',
  177. $email,
  178. ));
  179. return 1;
  180. }
  181. $user->setSystemEMailAddress($email);
  182. if ($this->appConfig->getValueString('core', 'newUser.sendEmail', 'yes') === 'yes') {
  183. try {
  184. $this->mailHelper->sendMail($user, $this->mailHelper->generateTemplate($user, true));
  185. $output->writeln('Welcome email sent to ' . $email);
  186. } catch (\Exception $e) {
  187. $output->writeln('Unable to send the welcome email to ' . $email);
  188. }
  189. }
  190. }
  191. return 0;
  192. }
  193. }