diff --git modules/user/user.module modules/user/user.module index b5115e9..37cc605 100644 --- modules/user/user.module +++ modules/user/user.module @@ -941,6 +941,31 @@ function user_account_form(&$form, &$form_state) { '#size' => 25, '#description' => t('To change the current user password, enter the new password in both fields.'), ); + // To skip the current password field, the user must have logged in via a + // one-time link and have the token in the URL. + $pass_reset = isset($_SESSION['pass_reset_'. $account->uid]) && isset($_GET['pass-reset-token']) && ($_GET['pass-reset-token'] == $_SESSION['pass_reset_'. $account->uid]); + $protected_values = array(); + $current_pass_description = ''; + // The user may only change their own password without their current + // password and only if they logged in via a one-time login link. + $not_my_account = ($user->uid != $account->uid); + if ($not_my_account || !$pass_reset) { + $protected_values['mail'] = $form['account']['mail']['#title']; + $protected_values['pass'] = t('Password'); + $current_pass_descriptions = t('Enter your current password to change the %mail or %pass.', array('%mail' => $protected_values['mail'], '%pass' => $protected_values['pass'])); + } + $form['account']['current_pass_required_values'] = array( + '#type' => 'value', + '#value' => $protected_values, + ); + $form['account']['current_pass'] = array( + '#type' => 'password', + '#title' => $not_my_account ? t('Your own current password') : t('Current password'), + '#size' => 25, + '#access' => !empty($protected_values), + '#description' => $current_pass_description, + ); + $form['#validate'][] = 'user_validate_current_pass'; } elseif (!variable_get('user_email_verification', TRUE) || $admin) { $form['account']['pass'] = array( @@ -1037,6 +1062,30 @@ function user_account_form(&$form, &$form_state) { } /** + * Form validation handler for the current password on the user_account_form(). + */ +function user_validate_current_pass(&$form, &$form_state) { + global $user; + + $account = $form['#user']; + foreach ($form_state['values']['current_pass_required_values'] as $key => $name) { + // This validation only works for required textfields (like mail) or + // form values like password_confirm that have their own validation + // that prevent them from being empty if they are changed. + if ((strlen(trim($form_state['values'][$key])) > 0) && ($form_state['values'][$key] != $account->$key)) { + require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); + $current_pass_failed = empty($form_state['values']['current_pass']) || !user_check_password($form_state['values']['current_pass'], $user); + if ($current_pass_failed) { + form_set_error('current_pass', t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => $name))); + form_set_error($key); + } + // We only need to check the password once. + break; + } + } +} + +/** * Form validation handler for user_account_form(). */ function user_account_form_validate($form, &$form_state) { @@ -3081,7 +3130,6 @@ function user_block_user_action(&$object, $context = array()) { * @ingroup forms * @see user_account_form() * @see user_account_form_validate() - * @see user_account_form_submit() * @see user_register_submit() */ function user_register_form($form, &$form_state) { diff --git modules/user/user.pages.inc modules/user/user.pages.inc index 32103c0..c8f6805 100644 --- modules/user/user.pages.inc +++ modules/user/user.pages.inc @@ -120,7 +120,10 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a // user, which invalidates further use of the one-time login link. user_login_finalize(); drupal_set_message(t('You have just used your one-time login link. It is no longer necessary to use this link to login. Please change your password.')); - drupal_goto('user/' . $user->uid . '/edit'); + // Let the user's password be changed without the current password check. + $token = md5(drupal_random_bytes(55)); + $_SESSION['pass_reset_'. $user->uid] = $token; + drupal_goto('user/' . $user->uid . '/edit', array('query' => array('pass-reset-token' => $token))); } else { $form['message'] = array('#markup' => t('

This is a one-time login for %user_name and will expire on %expiration_date.

Click on this button to login to the site and change your password.

', array('%user_name' => $account->name, '%expiration_date' => format_date($timestamp + $timeout)))); @@ -219,7 +222,6 @@ function template_preprocess_user_profile_category(&$variables) { * @ingroup forms * @see user_account_form() * @see user_account_form_validate() - * @see user_account_form_submit() * @see user_profile_form_validate() * @see user_profile_form_submit() * @see user_cancel_confirm_form_submit() @@ -283,6 +285,10 @@ function user_profile_form_submit($form, &$form_state) { user_save($account, $edit, $category); $form_state['values']['uid'] = $account->uid; + if ($category == 'account' && !empty($edit['pass'])) { + // Remove the password reset tag since a new password was saved. + unset($_SESSION['pass_reset_'. $account->uid]); + } // Clear the page cache because pages can contain usernames and/or profile information: cache_clear_all(); diff --git modules/user/user.test modules/user/user.test index d9f0c45..e8df16b 100644 --- modules/user/user.test +++ modules/user/user.test @@ -1339,5 +1339,30 @@ class UserEditTestCase extends DrupalWebTestCase { variable_set('user_pictures', 1); $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); $this->assertRaw(t('The name %name is already taken.', array('%name' => $edit['name']))); + + // Test that error message appears when attempting to change mail or passs + // without the current password. + $edit = array(); + $edit['mail'] = $this->randomName() . '@new.example.com'; + $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); + $this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('E-mail address')))); + + $edit['current_pass'] = $user1->pass_raw; + $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); + $this->assertRaw(t("The changes have been saved.")); + + $edit = array(); + $edit['pass[pass1]'] = $new_pass = $this->randomName(); + $edit['pass[pass2]'] = $new_pass; + $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); + $this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('Password')))); + + $edit['current_pass'] = $user1->pass_raw; + $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); + $this->assertRaw(t("The changes have been saved.")); + // Make sure the user can log in with their new password. + $this->drupalLogout(); + $user1->pass_raw = $new_pass; + $this->drupalLogin($user1); } }