diff --git a/core/modules/user/src/Controller/UserController.php b/core/modules/user/src/Controller/UserController.php index 0243c33..3956e27 100644 --- a/core/modules/user/src/Controller/UserController.php +++ b/core/modules/user/src/Controller/UserController.php @@ -8,6 +8,7 @@ namespace Drupal\user\Controller; use Drupal\Component\Utility\Xss; +use Drupal\Component\Utility\Crypt; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Datetime\DateFormatter; use Drupal\user\UserDataInterface; @@ -88,12 +89,22 @@ public static function create(ContainerInterface $container) { public function resetPass($uid, $timestamp, $hash) { $account = $this->currentUser(); $config = $this->config('user.settings'); - // When processing the one-time login link, we have to make sure that a user - // isn't already logged in. + if ($account->isAuthenticated()) { // The current user is already logged in. if ($account->id() == $uid) { - drupal_set_message($this->t('You are logged in as %user. Change your password.', array('%user' => $account->getUsername(), '@user_edit' => $this->url('entity.user.edit_form', array('user' => $account->id()))))); + // Add a session token to the link to let the user change their password + // without having to enter their current password, since they may not + // know it. + $token = Crypt::randomBytesBase64(55); + $_SESSION['pass_reset_' . $account->id()] = $token; + drupal_set_message($this->t('You are logged in as %user. Change your password.', array( + '%user' => $account->getUsername(), + '@user_edit' => $this->url('entity.user.edit_form', array( + 'user' => $account->id(), + 'pass-reset-token' => $token, + )), + ))); } // A different user is already logged in on the computer. else { diff --git a/core/modules/user/src/Form/UserPasswordForm.php b/core/modules/user/src/Form/UserPasswordForm.php index 48b5571..b7fc378 100644 --- a/core/modules/user/src/Form/UserPasswordForm.php +++ b/core/modules/user/src/Form/UserPasswordForm.php @@ -92,7 +92,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['name']['#value'] = $user->getEmail(); $form['mail'] = array( '#prefix' => '

', - '#markup' => $this->t('Password reset instructions will be mailed to %email. You must log out to use the password reset link in the email.', array('%email' => $user->getEmail())), + '#markup' => $this->t('Password reset instructions will be mailed to %email. You must log out to use the password reset link in the email.', ['%email' => $user->getEmail()]), '#suffix' => '

', ); } diff --git a/core/modules/user/src/Tests/UserPasswordResetTest.php b/core/modules/user/src/Tests/UserPasswordResetTest.php index c48199a..8bfde5a 100644 --- a/core/modules/user/src/Tests/UserPasswordResetTest.php +++ b/core/modules/user/src/Tests/UserPasswordResetTest.php @@ -51,21 +51,16 @@ protected function setUp() { $this->drupalPlaceBlock('system_menu_block:account'); // Create a user. - $account = $this->drupalCreateUser(); + $this->account = $this->drupalCreateUser(); // Activate user by logging in. - $this->drupalLogin($account); - - $this->account = user_load($account->id()); + $this->drupalLogin($this->account); $this->drupalLogout(); // Set the last login time that is used to generate the one-time link so // that it is definitely over a second ago. - $account->login = REQUEST_TIME - mt_rand(10, 100000); - db_update('users_field_data') - ->fields(array('login' => $account->getLastLoginTime())) - ->condition('uid', $account->id()) - ->execute(); + $this->account->setLastLoginTime(REQUEST_TIME - mt_rand(10, 100000)); + $this->account->save(); } /** @@ -85,7 +80,7 @@ function testUserPasswordReset() { $edit['name'] = $this->account->getUsername(); $this->drupalPostForm(NULL, $edit, t('Submit')); - // Verify that the user was sent an email. + // Verify that the user was sent an email. $this->assertMail('to', $this->account->getEmail(), 'Password email sent to user.'); $subject = t('Replacement login information for @username at @site', array('@username' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name'))); $this->assertMail('subject', $subject, 'Password reset email subject is correct.'); @@ -159,12 +154,40 @@ function testUserPasswordReset() { */ public function getResetURL() { // Assume the most recent email. - $_emails = $this->drupalGetMails(); - $email = end($_emails); + $emails = $this->drupalGetMails(); + $email = end($emails); $urls = array(); preg_match('#.+user/reset/.+#', $email['body'], $urls); - return $urls[0]; + return reset($urls); + } + + + /** + * Test user password reset while logged in. + */ + public function testUserPasswordResetLoggedIn() { + // Log in. + $this->drupalLogin($this->account); + + // Reset the password by username via the password reset page. + $this->drupalGet('user/password'); + $this->drupalPostForm(NULL, NULL, t('Submit')); + + // Click the reset URL while logged and change our password. + $resetURL = $this->getResetURL(); + $this->drupalGet($resetURL); + $this->clickLink(t('Change your password.')); + + // Make sure we do not need to enter the current password if we go to the + // "reset password" link while logged in. + $this->assertNoRaw(t('Current password')); + + // Change the password. + $password = user_password(); + $edit = array('pass[pass1]' => $password, 'pass[pass2]' => $password); + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->assertText(t('The changes have been saved.'), 'Password changed.'); } /**