- <?php
- * @file
- * Tests for resetting the password.
- */
-
- class UserPasswordResetTest extends BackdropWebTestCase {
- protected $profile = 'testing';
-
-
- * @var User
- */
- protected $account;
-
- function setUp($modules = array()) {
- $modules = array_merge($modules, array('image'));
- parent::setUp($modules);
-
-
- $account = $this->backdropCreateUser(array());
-
-
- $this->backdropLogin($account);
-
- $this->account = user_load($account->uid, TRUE);
- $this->backdropLogout();
-
-
-
- $account->login = REQUEST_TIME - mt_rand(10, 100000);
- db_update('users')
- ->fields(array('login' => $account->login))
- ->condition('uid', $account->uid)
- ->execute();
- }
-
-
- * Retrieves password reset email and extracts the login link.
- */
- protected function getResetURL() {
-
- $_emails = $this->backdropGetMails();
- $email = end($_emails);
- $urls = array();
- preg_match('#.+user/reset/.+#', $email['body'], $urls);
-
- return $urls[0];
- }
-
-
- * Tests password reset functionality.
- */
- public function testUserPasswordReset() {
-
- $this->backdropGet('user/password');
-
- $edit = array('name' => $this->randomName(32));
- $this->backdropPost(NULL, $edit, t('Reset password'));
-
- $this->assertRaw(t('Sorry, %name is not recognized as a user name or an email address.', array('%name' => $edit['name'])), 'Validation error message shown when trying to request password for invalid account.');
- $this->assertEqual(count($this->backdropGetMails(array('id' => 'user_password_reset'))), 0, 'No email was sent when requesting a password for an invalid account.');
-
-
- $edit['name'] = $this->account->name;
- $this->backdropPost(NULL, $edit, t('Reset password'));
-
-
- $this->assertMail('to', $this->account->mail, 'Password email sent to user.');
- $subject = t('Password reset information for @username at @site', array('@username' => $this->account->name, '@site' => config_get('system.core', 'site_name')));
- $this->assertMail('subject', $subject, 'Password reset email subject is correct.');
-
-
- $this->assertNoText('Sorry, too many password reset attempts', 'Flood control was not triggered by single password reset.');
-
- $resetURL = $this->getResetURL();
- $this->backdropGet($resetURL);
-
-
- $this->backdropPost(NULL, array(), t('Save password & log in'));
- $this->assertText(t('Password field is required.'));
-
-
- $pass = user_password();
- $pass_edit = array(
- 'pass[pass1]' => $pass,
- 'pass[pass2]' => $pass,
- );
- $this->backdropPost(NULL, $pass_edit, t('Save password & log in'));
- $this->assertLink(t('Log out'));
- $this->assertUrl('<front>');
-
-
- $this->backdropLogout();
- $this->backdropGet($resetURL);
- $this->assertText(t('You have tried to use a reset password link that has either been used or is no longer valid. Please request a new one using the form below.'), 'One-time link is no longer valid.');
-
-
- $this->backdropGet('user/password');
-
- $before = count($this->backdropGetMails(array('id' => 'user_password_reset')));
- $edit['name'] = $this->account->mail;
- $this->backdropPost(NULL, $edit, t('Reset password'));
- $this->assertTrue( count($this->backdropGetMails(array('id' => 'user_password_reset'))) === $before + 1, 'Email sent when requesting password reset using email address.');
-
-
- $user_pass_reset_url = $this->getResetURL();
- $new_password = user_password();
- $edit = array(
- 'pass[pass1]' => $new_password,
- 'pass[pass2]' => $new_password,
- );
- $this->backdropPost($user_pass_reset_url, $edit, t('Save password & log in'));
- $this->assertText(t('Your account password has been updated.'), 'One time login with password reset completed.');
- $this->account = user_load($this->account->uid, TRUE);
- $this->assertTrue(user_check_password($new_password, $this->account), 'Password reset successful.');
- $this->backdropLogout();
-
-
- $timeout = 86400;
- $bogus_timestamp = REQUEST_TIME - $timeout - 60;
- $this->backdropGet("user/reset/{$this->account->uid}/$bogus_timestamp/" . user_pass_rehash($this->account->pass, $bogus_timestamp, $this->account->login, $this->account->uid, $this->account->mail));
- $this->assertText(t('You have tried to use a reset password link that has expired. Please request a new one using the form below.'), 'Expired password reset request rejected.');
- $this->backdropLogout();
-
-
- sleep(1);
- $timestamp = time();
- $this->backdropGet("user/reset/{$this->account->uid}/$timestamp/" . user_pass_rehash($this->account->pass, $timestamp, $this->account->login, $this->account->uid, $this->account->mail) . '/login');
- $this->assertText(t('You have used your one-time log-in link and are now logged-in.'), 'Immediate login link message shown.');
- $this->backdropGet("user/{$this->account->uid}/edit");
- $this->assertResponse(200, 'Immediate login link logged user in.');
- }
-
-
- * Attempts login using an expired password reset link.
- */
- public function testUserPasswordResetExpired() {
-
- $timeout = 43200;
- config_set('system.core', 'user_password_reset_timeout', $timeout);
-
-
- $account = $this->backdropCreateUser();
- $this->backdropLogin($account);
-
- $account = user_load($account->uid, TRUE);
- $this->backdropLogout();
-
-
-
- $bogus_timestamp = REQUEST_TIME - config_get('system.core', 'user_password_reset_timeout') - 60;
- $this->backdropGet("user/reset/$account->uid/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid, $account->mail));
- $this->assertText(t('You have tried to use a reset password link that has expired. Please request a new one using the form below.'), 'Expired password reset request rejected.');
- }
-
-
- * Prefill the text box on incorrect login via link to password reset page.
- */
- public function testUserResetPasswordTextboxFilled() {
- $this->backdropGet('user/login');
- $edit = array(
- 'name' => $this->randomName(),
- 'pass' => $this->randomName(),
- );
- $this->backdropPost('user', $edit, t('Log in'));
- $this->assertText(t('Sorry, unrecognized username.'));
- unset($edit['pass']);
- $this->backdropGet('user/password', array('query' => array('name' => $edit['name'])));
- $this->assertFieldByName('name', $edit['name'], 'User name pre-populated into name field.');
- }
-
-
- * Make sure that users cannot forge password reset URLs of other users.
- */
- public function testResetImpersonation() {
-
-
- $account = user_load(1);
- $account->pass = user_password();
- $account->save();
-
-
-
-
- $user1 = $this->backdropCreateUser();
- $user2 = $this->backdropCreateUser();
- db_update('users')
- ->fields(array('pass' => ''))
- ->condition('uid', array($user1->uid, $user2->uid), 'IN')
- ->execute();
- $user1 = user_load($user1->uid);
- $user2 = user_load($user2->uid);
-
-
-
- $reset_url = user_pass_reset_url($user1);
- $attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url);
- $this->backdropGet($attack_reset_url);
- $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
- $this->assertText('You have tried to use a reset password link that has either been used or is no longer valid. Please request a new one using the form below.');
-
-
-
-
-
- $timestamp = REQUEST_TIME;
-
-
-
- $reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
- $reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE));
- $this->backdropGet($reset_url);
- $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
- $this->assertText('You have tried to use a reset password link that has either been used or is no longer valid. Please request a new one using the form below.');
- $attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url);
- $this->backdropGet($attack_reset_url);
- $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
- $this->assertText('You have tried to use a reset password link that has either been used or is no longer valid. Please request a new one using the form below.');
-
-
-
-
-
- $new_reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
- $this->assertNotEqual($reset_url_token, $new_reset_url_token);
-
-
-
- user_delete($user2->uid);
- $reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
- $reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE));
- $this->backdropGet($reset_url);
- $this->assertUrl($reset_url, array(), 'The user remains on the password reset login page.');
- $this->assertNoText('You have tried to use a reset password link that has either been used or is no longer valid. Please request a new one using the form below.');
- }
-
-
- * Test user-based flood control on password reset.
- */
- public function testPasswordResetFloodControlPerUser() {
-
- $limit = 2;
- config_set('user.flood', 'flood_user_limit', $limit);
-
-
- $account = $this->backdropCreateUser();
- $this->backdropLogin($account);
- $this->backdropLogout();
-
- $edit = array('name' => $account->name);
-
-
- for ($i = 0; $i < $limit; $i++) {
- $this->backdropPost('user/password', $edit, t('Reset password'));
-
- $this->assertText(t('Further instructions have been sent to your email address.'), 'Password reset instructions mailed message displayed.');
-
- $this->assertNoText('Sorry, too many password reset attempts', 'Flood control was not triggered by password reset.');
- }
-
-
- $resetURL = $this->getResetURL();
- $this->backdropGet($resetURL);
-
-
- $new_password = $this->randomName(20);
- $pass_edit = array(
- 'pass[pass1]' => $new_password,
- 'pass[pass2]' => $new_password,
- );
- $this->backdropPost(NULL, $pass_edit, t('Save password & log in'));
- $this->backdropLogout();
-
-
- for ($i = 0; $i < $limit; $i++) {
- $this->backdropPost('user/password', $edit, t('Reset password'));
-
- $this->assertText(t('Further instructions have been sent to your email address.'), 'Password reset instructions mailed message displayed.');
-
- $this->assertNoText('Sorry, too many password reset attempts', 'Flood control was not triggered by password reset.');
- }
-
-
- $this->backdropPost('user/password', $edit, t('Reset password'));
-
- $this->assertNoText(t('Further instructions have been sent to your email address.'), 'Password reset instructions mailed message not displayed for excessive password resets.');
-
- $this->assertText('Sorry, too many password reset attempts', 'Flood control was triggered by excessive password resets for one user.');
- }
-
-
- * Test IP-based flood control on password reset.
- */
- public function testPasswordResetFloodControlPerIp() {
-
- $limit = 2;
- config_set('user.flood', 'flood_ip_limit', $limit);
-
-
- for ($i = 0; $i < $limit; $i++) {
- $name = $this->randomName();
- $edit = array('name' => $name);
- $this->backdropPost('user/password', $edit, t('Reset password'));
-
-
- $this->assertText(t('Sorry, @name is not recognized as a user name or an email address.', array('@name' => $name)), 'User name not recognized message displayed.');
-
- $this->assertNoText('Sorry, too many password reset attempts', 'Flood control was not triggered by password reset.');
- }
-
-
- $name = $this->randomName();
- $edit = array('name' => $name);
- $this->backdropPost('user/password', $edit, t('Reset password'));
-
-
- $this->assertNoText(t('Sorry, @name is not recognized as a user name or an email address.', array('@name' => $name)), 'User name not recognized message not displayed.');
-
- $this->assertText('Sorry, too many password reset attempts', 'Flood control was triggered by excessive password resets from one IP.');
- }
-
-
- * Make sure that password reset URLs are invalidated when the user's email
- * address changes.
- */
- public function testResetInvalidation() {
- $account = $this->backdropCreateUser();
- $original_reset_url = user_pass_reset_url($account);
- $account->mail = '1' . $account->mail;
- $account->save();
- $this->backdropGet($original_reset_url);
- $this->assertText('You have tried to use a reset password link that has either been used or is no longer valid. Please request a new one using the form below.');
- }
-
-
- * Test uniqueness of output from user_pass_rehash() with no passwords.
- */
- public function testUniqueHashNoPasswordValue() {
- $timestamp = REQUEST_TIME;
-
-
- $user = backdrop_anonymous_user();
- $user->login = $timestamp - 1000;
- $user->pass = '';
-
- $user_a = clone $user;
- $user_a->uid = 12;
- $user_a->mail = '3user@example.com';
-
- $user_b = clone $user;
- $user_b->uid = 123;
- $user_b->mail = 'user@example.com';
-
- $hash_a = user_pass_rehash($user_a->pass, $timestamp, $user_a->login, $user_a->uid, $user_a->mail);
- $hash_b = user_pass_rehash($user_b->pass, $timestamp, $user_b->login, $user_b->uid, $user_b->mail);
-
- $this->assertNotEqual($hash_a, $hash_b, "No user_pass_rehash() hash collision for different users with no stored password.");
- }
-
-
- * Test user password reset in a browser where another user is logged in.
- */
- public function testUserPasswordResetOtherUserLoggedIn() {
- $another_account = $this->backdropCreateUser();
- $account = $this->backdropCreateUser();
- $this->backdropLogin($account);
-
- user_save($account, array('pass' => user_password()));
-
-
-
- $reset_url = user_pass_reset_url($another_account);
- $this->backdropGet($reset_url);
- $this->assertRaw(t(
- 'You cannot use a password reset link while logged into the site. Please <a href="!logout">logout</a> and try using the link again.',
- array('!logout' => url('user/logout'))
- ));
-
-
- $attack_reset_url = "user/reset/" . $another_account->uid . "/1/1";
- $this->backdropGet($attack_reset_url);
- $this->assertNoText($another_account->name);
- $this->assertText('The one-time login link you clicked is invalid.');
- }
-
-
- * Test user password reset in a browser where the same user is already logged
- * in.
- */
- public function testUserPasswordResetAlreadyLoggedIn() {
- $account = $this->backdropCreateUser();
- $this->backdropLogin($account);
-
- $account->pass = user_password();
- $account->save();
-
-
- $reset_url = user_pass_reset_url($account);
- $this->backdropGet($reset_url);
-
- $this->assertText('Reset password');
- $pass = user_password();
- $edit = array(
- 'pass[pass1]' => $pass,
- 'pass[pass2]' => $pass,
- );
- $this->backdropPost(NULL, $edit, t('Save password & log in'));
-
- $this->assertText('Your account password has been updated.');
- }
-
- }