NewUserCommand.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <?php
  2. /**
  3. * @package Grav\Plugin\Login
  4. *
  5. * @copyright Copyright (C) 2014 - 2021 RocketTheme, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Plugin\Console;
  9. use Grav\Common\User\Interfaces\UserCollectionInterface;
  10. use Grav\Console\ConsoleCommand;
  11. use Grav\Common\Grav;
  12. use Grav\Plugin\Login\Login;
  13. use Symfony\Component\Console\Input\InputOption;
  14. use Symfony\Component\Console\Helper\Helper;
  15. use Symfony\Component\Console\Question\ChoiceQuestion;
  16. use Symfony\Component\Console\Question\Question;
  17. /**
  18. * Class CleanCommand
  19. *
  20. * @package Grav\Console\Cli
  21. */
  22. class NewUserCommand extends ConsoleCommand
  23. {
  24. /** @var array */
  25. protected $options = [];
  26. /** @var Login */
  27. protected $login;
  28. /**
  29. * Configure the command
  30. */
  31. protected function configure()
  32. {
  33. $this
  34. ->setName('new-user')
  35. ->setAliases(['add-user', 'newuser'])
  36. ->addOption(
  37. 'user',
  38. 'u',
  39. InputOption::VALUE_REQUIRED,
  40. 'The username'
  41. )
  42. ->addOption(
  43. 'password',
  44. 'p',
  45. InputOption::VALUE_REQUIRED,
  46. "The password. Note that this option is not recommended because the password will be visible by users listing the processes. You should also make sure the password respects Grav's password policy."
  47. )
  48. ->addOption(
  49. 'email',
  50. 'e',
  51. InputOption::VALUE_REQUIRED,
  52. 'The user email'
  53. )
  54. ->addOption(
  55. 'language',
  56. 'l',
  57. InputOption::VALUE_OPTIONAL,
  58. 'The default language of the account. [default: "en"]'
  59. )
  60. ->addOption(
  61. 'permissions',
  62. 'P',
  63. InputOption::VALUE_REQUIRED,
  64. 'The user permissions. It can be either `a` for Admin access only, `s` for Site access only and `b` for both Admin and Site access.'
  65. )
  66. ->addOption(
  67. 'fullname',
  68. 'N',
  69. InputOption::VALUE_REQUIRED,
  70. 'The user full name.'
  71. )
  72. ->addOption(
  73. 'title',
  74. 't',
  75. InputOption::VALUE_REQUIRED,
  76. 'The title of the user. Usually used as a subtext. Example: Admin, Collaborator, Developer'
  77. )
  78. ->addOption(
  79. 'state',
  80. 's',
  81. InputOption::VALUE_REQUIRED,
  82. 'The state of the account. Can be either `enabled` or `disabled`. [default: "enabled"]'
  83. )
  84. ->setDescription('Creates a new user')
  85. ->setHelp('The <info>new-user</info> creates a new user file in user/accounts/ folder')
  86. ;
  87. }
  88. /**
  89. * @return int|null|void
  90. */
  91. protected function serve()
  92. {
  93. include __DIR__ . '/../vendor/autoload.php';
  94. $grav = Grav::instance();
  95. if (!isset($grav['login'])) {
  96. $grav['login'] = new Login($grav);
  97. }
  98. $this->login = $grav['login'];
  99. $this->options = [
  100. 'user' => $this->input->getOption('user'),
  101. 'password1' => $this->input->getOption('password'),
  102. 'email' => $this->input->getOption('email'),
  103. 'language' => $this->input->getOption('language'),
  104. 'permissions' => $this->input->getOption('permissions'),
  105. 'fullname' => $this->input->getOption('fullname'),
  106. 'title' => $this->input->getOption('title'),
  107. 'state' => $this->input->getOption('state')
  108. ];
  109. $this->validateOptions();
  110. $helper = $this->getHelper('question');
  111. $data = [];
  112. $this->output->writeln('<green>Creating new user</green>');
  113. $this->output->writeln('');
  114. /** @var UserCollectionInterface $users */
  115. $users = $grav['accounts'];
  116. if (!$this->options['user']) {
  117. // Get username and validate
  118. $question = new Question('Enter a <yellow>username</yellow>: ', 'admin');
  119. $question->setValidator(function ($value) use ($users) {
  120. $this->validate('user', $value);
  121. if ($users->find($value, ['username'])->exists()) {
  122. throw new \RuntimeException('Username "' . $value . '" already exists, please pick another username');
  123. };
  124. return $value;
  125. });
  126. $username = $helper->ask($this->input, $this->output, $question);
  127. } else {
  128. $username = $this->options['user'];
  129. }
  130. $user = $users->load($username);
  131. if ($user->exists()) {
  132. $this->output->writeln('<red>Failure!</red> User <cyan>' . $data['username'] . '</cyan> already exists!');
  133. exit();
  134. }
  135. if (!$this->options['password1']) {
  136. // Get password and validate
  137. $password = $this->askForPassword($helper, 'Enter a <yellow>password</yellow>: ', function ($password1) use ($helper) {
  138. $this->validate('password1', $password1);
  139. // Since input is hidden when prompting for passwords, the user is asked to repeat the password
  140. return $this->askForPassword($helper, 'Repeat the <yellow>password</yellow>: ', function ($password2) use ($password1) {
  141. return $this->validate('password2', $password2, $password1);
  142. });
  143. });
  144. $user->set('password', $password);
  145. } else {
  146. $user->set('password', $this->options['password1']);
  147. }
  148. if (!$this->options['email']) {
  149. // Get email and validate
  150. $question = new Question('Enter an <yellow>email</yellow>: ');
  151. $question->setValidator(function ($value) {
  152. return $this->validate('email', $value);
  153. });
  154. $user->set('email', $helper->ask($this->input, $this->output, $question));
  155. } else {
  156. $user->set('email', $this->options['email']);
  157. }
  158. if (!$this->options['language']) {
  159. // Get language and validate.
  160. $question = new Question('Enter a <yellow>language abbreviation</yellow> [en]: ');
  161. $question->setValidator(function ($value) {
  162. return $this->validate('language', $value);
  163. });
  164. $user->set('language', $helper->ask($this->input, $this->output, $question));
  165. if ($user->get('language') == null) {
  166. $user->set('language', 'en');
  167. }
  168. } else {
  169. $user->set('language', $this->options['language']);
  170. }
  171. if (!$this->options['permissions']) {
  172. // Choose permissions
  173. $question = new ChoiceQuestion(
  174. 'Please choose a set of <yellow>permissions</yellow>:',
  175. array('a' => 'Admin Access', 's' => 'Site Access', 'b' => 'Admin and Site Access'),
  176. 'a'
  177. );
  178. $question->setErrorMessage('Permissions %s is invalid.');
  179. $permissions_choice = $helper->ask($this->input, $this->output, $question);
  180. } else {
  181. $permissions_choice = $this->options['permissions'];
  182. }
  183. switch ($permissions_choice) {
  184. case 'a':
  185. $access = [
  186. 'admin' => [
  187. 'login' => true,
  188. 'super' => true
  189. ]
  190. ];
  191. break;
  192. case 's':
  193. $access = [
  194. 'site' => [
  195. 'login' => true
  196. ]
  197. ];
  198. break;
  199. case 'b':
  200. $access = [
  201. 'admin' => [
  202. 'login' => true,
  203. 'super' => true
  204. ],
  205. 'site' => [
  206. 'login' => true
  207. ]
  208. ];
  209. }
  210. if (isset($access)) {
  211. $user->set('access', $access);
  212. }
  213. if (!$this->options['fullname']) {
  214. // Get fullname
  215. $question = new Question('Enter a <yellow>fullname</yellow>: ');
  216. $question->setValidator(function ($value) {
  217. return $this->validate('fullname', $value);
  218. });
  219. $user->set('fullname', $helper->ask($this->input, $this->output, $question));
  220. } else {
  221. $user->set('fullname', $this->options['fullname']);
  222. }
  223. if (!$this->options['title'] && !count(array_filter($this->options))) {
  224. // Get title
  225. $question = new Question('Enter a <yellow>title</yellow>: ');
  226. $user->set('title', $helper->ask($this->input, $this->output, $question));
  227. } else {
  228. $user->set('title', $this->options['title']);
  229. }
  230. if (!$this->options['state'] && !count(array_filter($this->options))) {
  231. // Choose State
  232. $question = new ChoiceQuestion(
  233. 'Please choose the <yellow>state</yellow> for the account:',
  234. array('enabled' => 'Enabled', 'disabled' => 'Disabled'),
  235. 'enabled'
  236. );
  237. $question->setErrorMessage('State %s is invalid.');
  238. $user->set('state', $helper->ask($this->input, $this->output, $question));
  239. } else {
  240. $user->set('state', $this->options['state'] ?: 'enabled');
  241. }
  242. $user->validate();
  243. $user->save();
  244. $this->invalidateCache();
  245. $this->output->writeln('');
  246. $this->output->writeln('<green>Success!</green> User <cyan>' . $user->username . '</cyan> created.');
  247. }
  248. /**
  249. *
  250. */
  251. protected function validateOptions()
  252. {
  253. foreach (array_filter($this->options) as $type => $value) {
  254. $this->validate($type, $value);
  255. }
  256. }
  257. /**
  258. * @param string $type
  259. * @param mixed $value
  260. * @param string $extra
  261. *
  262. * @return string
  263. */
  264. protected function validate($type, $value, $extra = '')
  265. {
  266. return $this->login->validateField($type, $value, $extra);
  267. }
  268. /**
  269. * Get password and validate.
  270. *
  271. * @param Helper $helper
  272. * @param string $question
  273. * @param callable $validator
  274. *
  275. * @return string
  276. */
  277. protected function askForPassword(Helper $helper, $question, callable $validator)
  278. {
  279. $question = new Question($question);
  280. $question->setValidator($validator);
  281. $question->setHidden(true);
  282. $question->setHiddenFallback(true);
  283. return $helper->ask($this->input, $this->output, $question);
  284. }
  285. }