NewUserCommand.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <?php
  2. /**
  3. * @package Grav\Plugin\Login
  4. *
  5. * @copyright Copyright (C) 2014 - 2017 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. 'permissions',
  56. 'P',
  57. InputOption::VALUE_REQUIRED,
  58. '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.'
  59. )
  60. ->addOption(
  61. 'fullname',
  62. 'N',
  63. InputOption::VALUE_REQUIRED,
  64. 'The user full name.'
  65. )
  66. ->addOption(
  67. 'title',
  68. 't',
  69. InputOption::VALUE_REQUIRED,
  70. 'The title of the user. Usually used as a subtext. Example: Admin, Collaborator, Developer'
  71. )
  72. ->addOption(
  73. 'state',
  74. 's',
  75. InputOption::VALUE_REQUIRED,
  76. 'The state of the account. Can be either `enabled` or `disabled`. [default: "enabled"]'
  77. )
  78. ->setDescription('Creates a new user')
  79. ->setHelp('The <info>new-user</info> creates a new user file in user/accounts/ folder')
  80. ;
  81. }
  82. /**
  83. * @return int|null|void
  84. */
  85. protected function serve()
  86. {
  87. include __DIR__ . '/../vendor/autoload.php';
  88. $grav = Grav::instance();
  89. if (!isset($grav['login'])) {
  90. $grav['login'] = new Login($grav);
  91. }
  92. $this->login = $grav['login'];
  93. $this->options = [
  94. 'user' => $this->input->getOption('user'),
  95. 'password1' => $this->input->getOption('password'),
  96. 'email' => $this->input->getOption('email'),
  97. 'permissions' => $this->input->getOption('permissions'),
  98. 'fullname' => $this->input->getOption('fullname'),
  99. 'title' => $this->input->getOption('title'),
  100. 'state' => $this->input->getOption('state')
  101. ];
  102. $this->validateOptions();
  103. $helper = $this->getHelper('question');
  104. $data = [];
  105. $this->output->writeln('<green>Creating new user</green>');
  106. $this->output->writeln('');
  107. /** @var UserCollectionInterface $users */
  108. $users = $grav['accounts'];
  109. if (!$this->options['user']) {
  110. // Get username and validate
  111. $question = new Question('Enter a <yellow>username</yellow>: ', 'admin');
  112. $question->setValidator(function ($value) use ($users) {
  113. $this->validate('user', $value);
  114. if ($users->find($value, ['username'])->exists()) {
  115. throw new \RuntimeException('Username "' . $value . '" already exists, please pick another username');
  116. };
  117. return $value;
  118. });
  119. $username = $helper->ask($this->input, $this->output, $question);
  120. } else {
  121. $username = $this->options['user'];
  122. }
  123. $user = $users->load($username);
  124. if ($user->exists()) {
  125. $this->output->writeln('<red>Failure!</red> User <cyan>' . $data['username'] . '</cyan> already exists!');
  126. exit();
  127. }
  128. if (!$this->options['password1']) {
  129. // Get password and validate
  130. $password = $this->askForPassword($helper, 'Enter a <yellow>password</yellow>: ', function ($password1) use ($helper) {
  131. $this->validate('password1', $password1);
  132. // Since input is hidden when prompting for passwords, the user is asked to repeat the password
  133. return $this->askForPassword($helper, 'Repeat the <yellow>password</yellow>: ', function ($password2) use ($password1) {
  134. return $this->validate('password2', $password2, $password1);
  135. });
  136. });
  137. $user->set('password', $password);
  138. } else {
  139. $user->set('password', $this->options['password1']);
  140. }
  141. if (!$this->options['email']) {
  142. // Get email and validate
  143. $question = new Question('Enter an <yellow>email</yellow>: ');
  144. $question->setValidator(function ($value) {
  145. return $this->validate('email', $value);
  146. });
  147. $user->set('email', $helper->ask($this->input, $this->output, $question));
  148. } else {
  149. $user->set('email', $this->options['email']);
  150. }
  151. if (!$this->options['permissions']) {
  152. // Choose permissions
  153. $question = new ChoiceQuestion(
  154. 'Please choose a set of <yellow>permissions</yellow>:',
  155. array('a' => 'Admin Access', 's' => 'Site Access', 'b' => 'Admin and Site Access'),
  156. 'a'
  157. );
  158. $question->setErrorMessage('Permissions %s is invalid.');
  159. $permissions_choice = $helper->ask($this->input, $this->output, $question);
  160. } else {
  161. $permissions_choice = $this->options['permissions'];
  162. }
  163. switch ($permissions_choice) {
  164. case 'a':
  165. $access = [
  166. 'admin' => [
  167. 'login' => true,
  168. 'super' => true
  169. ]
  170. ];
  171. break;
  172. case 's':
  173. $access = [
  174. 'site' => [
  175. 'login' => true
  176. ]
  177. ];
  178. break;
  179. case 'b':
  180. $access = [
  181. 'admin' => [
  182. 'login' => true,
  183. 'super' => true
  184. ],
  185. 'site' => [
  186. 'login' => true
  187. ]
  188. ];
  189. }
  190. if (isset($access)) {
  191. $user->set('access', $access);
  192. }
  193. if (!$this->options['fullname']) {
  194. // Get fullname
  195. $question = new Question('Enter a <yellow>fullname</yellow>: ');
  196. $question->setValidator(function ($value) {
  197. return $this->validate('fullname', $value);
  198. });
  199. $user->set('fullname', $helper->ask($this->input, $this->output, $question));
  200. } else {
  201. $user->set('fullname', $this->options['fullname']);
  202. }
  203. if (!$this->options['title'] && !count(array_filter($this->options))) {
  204. // Get title
  205. $question = new Question('Enter a <yellow>title</yellow>: ');
  206. $user->set('title', $helper->ask($this->input, $this->output, $question));
  207. } else {
  208. $user->set('title', $this->options['title']);
  209. }
  210. if (!$this->options['state'] && !count(array_filter($this->options))) {
  211. // Choose State
  212. $question = new ChoiceQuestion(
  213. 'Please choose the <yellow>state</yellow> for the account:',
  214. array('enabled' => 'Enabled', 'disabled' => 'Disabled'),
  215. 'enabled'
  216. );
  217. $question->setErrorMessage('State %s is invalid.');
  218. $user->set('state', $helper->ask($this->input, $this->output, $question));
  219. } else {
  220. $user->set('state', $this->options['state'] ?: 'enabled');
  221. }
  222. $user->validate();
  223. $user->save();
  224. $this->invalidateCache();
  225. $this->output->writeln('');
  226. $this->output->writeln('<green>Success!</green> User <cyan>' . $user->username . '</cyan> created.');
  227. }
  228. /**
  229. *
  230. */
  231. protected function validateOptions()
  232. {
  233. foreach (array_filter($this->options) as $type => $value) {
  234. $this->validate($type, $value);
  235. }
  236. }
  237. /**
  238. * @param string $type
  239. * @param mixed $value
  240. * @param string $extra
  241. *
  242. * @return string
  243. */
  244. protected function validate($type, $value, $extra = '')
  245. {
  246. return $this->login->validateField($type, $value, $extra);
  247. }
  248. /**
  249. * Get password and validate.
  250. *
  251. * @param Helper $helper
  252. * @param string $question
  253. * @param callable $validator
  254. *
  255. * @return string
  256. */
  257. protected function askForPassword(Helper $helper, $question, callable $validator)
  258. {
  259. $question = new Question($question);
  260. $question->setValidator($validator);
  261. $question->setHidden(true);
  262. $question->setHiddenFallback(true);
  263. return $helper->ask($this->input, $this->output, $question);
  264. }
  265. }