ChangePasswordCommand.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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\Config\Config;
  10. use Grav\Console\ConsoleCommand;
  11. use Grav\Common\Grav;
  12. use Grav\Common\File\CompiledYamlFile;
  13. use Grav\Common\User\User;
  14. use Grav\Plugin\Login\Login;
  15. use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
  16. use Symfony\Component\Console\Input\InputOption;
  17. use Symfony\Component\Console\Helper\Helper;
  18. use Symfony\Component\Console\Question\Question;
  19. /**
  20. * Class CleanCommand
  21. *
  22. * @package Grav\Console\Cli
  23. */
  24. class ChangePasswordCommand extends ConsoleCommand
  25. {
  26. /** @var array */
  27. protected $options = [];
  28. /** @var Login */
  29. protected $login;
  30. /**
  31. * Configure the command
  32. */
  33. protected function configure()
  34. {
  35. $this
  36. ->setName('change-password')
  37. ->setAliases(['edit-password', 'newpass', 'changepass', 'passwd'])
  38. ->addOption(
  39. 'user',
  40. 'u',
  41. InputOption::VALUE_REQUIRED,
  42. 'The username'
  43. )
  44. ->addOption(
  45. 'password',
  46. 'p',
  47. InputOption::VALUE_REQUIRED,
  48. "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."
  49. )
  50. ->setDescription('Changes a User Password')
  51. ->setHelp('The <info>change-password</info> changes the password of the specified user. (User must exist)')
  52. ;
  53. }
  54. /**
  55. * @return int|null|void
  56. */
  57. protected function serve()
  58. {
  59. include __DIR__ . '/../vendor/autoload.php';
  60. $grav = Grav::instance();
  61. if (!isset($grav['login'])) {
  62. $grav['login'] = new Login($grav);
  63. }
  64. $this->login = $grav['login'];
  65. $this->options = [
  66. 'user' => $this->input->getOption('user'),
  67. 'password1' => $this->input->getOption('password')
  68. ];
  69. $this->validateOptions();
  70. $helper = $this->getHelper('question');
  71. $data = [];
  72. $this->output->writeln('<green>Changing User Password</green>');
  73. $this->output->writeln('');
  74. if (!$this->options['user']) {
  75. // Get username and validate
  76. $question = new Question('Enter a <yellow>username</yellow>: ');
  77. $question->setValidator(function ($value) {
  78. $this->validate('user', $value);
  79. if (!User::find($value, ['username'])->exists()) {
  80. throw new \RuntimeException('Username "' . $value . '" does not exist, please pick another username');
  81. };
  82. return true;
  83. });
  84. $username = $helper->ask($this->input, $this->output, $question);
  85. } else {
  86. $username = $this->options['user'];
  87. }
  88. if (!$this->options['password1']) {
  89. // Get password and validate
  90. $password = $this->askForPassword($helper, 'Enter a <yellow>new password</yellow>: ', function ($password1) use ($helper) {
  91. $this->validate('password1', $password1);
  92. // Since input is hidden when prompting for passwords, the user is asked to repeat the password
  93. return $this->askForPassword($helper, 'Repeat the <yellow>password</yellow>: ', function ($password2) use ($password1) {
  94. return $this->validate('password2', $password2, $password1);
  95. });
  96. });
  97. $data['password'] = $password;
  98. } else {
  99. $data['password'] = $this->options['password1'];
  100. }
  101. // Lowercase the username for the filename
  102. $username = strtolower($username);
  103. /** @var UniformResourceLocator $locator */
  104. $locator = Grav::instance()['locator'];
  105. // Grab the account file and read in the information before setting the file (prevent setting erase)
  106. $oldUserFile = CompiledYamlFile::instance($locator->findResource('account://' . $username . YAML_EXT, true, true));
  107. $oldData = (array)$oldUserFile->content();
  108. //Set the password field to new password
  109. $oldData['password'] = $data['password'];
  110. // Create user object and save it using oldData (with updated password)
  111. $user = new User($oldData);
  112. $file = CompiledYamlFile::instance($locator->findResource('account://' . $username . YAML_EXT, true, true));
  113. $user->file($file);
  114. $user->save();
  115. $this->output->writeln('');
  116. $this->output->writeln('<green>Success!</green> User <cyan>' . $username . '\'s</cyan> password changed.');
  117. }
  118. /**
  119. *
  120. */
  121. protected function validateOptions()
  122. {
  123. foreach (array_filter($this->options) as $type => $value) {
  124. $this->validate($type, $value);
  125. }
  126. }
  127. /**
  128. * @param string $type
  129. * @param mixed $value
  130. * @param string $extra
  131. *
  132. * @return string
  133. */
  134. protected function validate($type, $value, $extra = '')
  135. {
  136. return $this->login->validateField($type, $value, $extra);
  137. }
  138. /**
  139. * Get password and validate.
  140. *
  141. * @param Helper $helper
  142. * @param string $question
  143. * @param callable $validator
  144. *
  145. * @return string
  146. */
  147. protected function askForPassword(Helper $helper, $question, callable $validator)
  148. {
  149. $question = new Question($question);
  150. $question->setValidator($validator);
  151. $question->setHidden(true);
  152. $question->setHiddenFallback(true);
  153. return $helper->ask($this->input, $this->output, $question);
  154. }
  155. }