function UserPasswordResetTest::testResetImpersonation

Same name in other branches
  1. 9 core/modules/user/tests/src/Functional/UserPasswordResetTest.php \Drupal\Tests\user\Functional\UserPasswordResetTest::testResetImpersonation()
  2. 8.9.x core/modules/user/tests/src/Functional/UserPasswordResetTest.php \Drupal\Tests\user\Functional\UserPasswordResetTest::testResetImpersonation()
  3. 10 core/modules/user/tests/src/Functional/UserPasswordResetTest.php \Drupal\Tests\user\Functional\UserPasswordResetTest::testResetImpersonation()

Make sure that users cannot forge password reset URLs of other users.

File

core/modules/user/tests/src/Functional/UserPasswordResetTest.php, line 604

Class

UserPasswordResetTest
Ensure that password reset methods work as expected.

Namespace

Drupal\Tests\user\Functional

Code

public function testResetImpersonation() : void {
    // Create two identical user accounts except for the user name. They must
    // have the same empty password, so we can't use $this->drupalCreateUser().
    $edit = [];
    $edit['name'] = $this->randomMachineName();
    $edit['mail'] = $edit['name'] . '@example.com';
    $edit['status'] = 1;
    $user1 = User::create($edit);
    $user1->save();
    $edit['name'] = $this->randomMachineName();
    $user2 = User::create($edit);
    $user2->save();
    // Unique password hashes are automatically generated, the only way to
    // change that is to update it directly in the database.
    Database::getConnection()->update('users_field_data')
        ->fields([
        'pass' => NULL,
    ])
        ->condition('uid', [
        $user1->id(),
        $user2->id(),
    ], 'IN')
        ->execute();
    \Drupal::entityTypeManager()->getStorage('user')
        ->resetCache();
    $user1 = User::load($user1->id());
    $user2 = User::load($user2->id());
    $this->assertEquals($user2->getPassword(), $user1->getPassword(), 'Both users have the same password hash.');
    // The password reset URL must not be valid for the second user when only
    // the user ID is changed in the URL.
    $reset_url = user_pass_reset_url($user1);
    $attack_reset_url = str_replace("user/reset/{$user1->id()}", "user/reset/{$user2->id()}", $reset_url);
    $this->drupalGet($attack_reset_url);
    // Verify that the invalid password reset page does not show the user name.
    $this->assertSession()
        ->pageTextNotContains($user2->getAccountName());
    $this->assertSession()
        ->addressEquals('user/password');
    $this->assertSession()
        ->pageTextContains('You have tried to use a one-time login link that has either been used or is no longer valid. Request a new one using the form below.');
    $this->drupalGet($attack_reset_url . '/login');
    // Verify that the invalid password reset page does not show the user name.
    $this->assertSession()
        ->pageTextNotContains($user2->getAccountName());
    $this->assertSession()
        ->addressEquals('user/password');
    $this->assertSession()
        ->pageTextContains('You have tried to use a one-time login link that has either been used or is no longer valid. Request a new one using the form below.');
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.