#86299 by pwolanin, neochief, fwalch, thePanz, et al: Security hardening: Add 'current password' field to 'change password form'.
parent
ae716002a3
commit
d1c261a8c5
|
@ -934,6 +934,7 @@ function user_account_form(&$form, &$form_state) {
|
|||
'#attributes' => array('class' => array('username')),
|
||||
'#default_value' => (!$register ? $account->name : ''),
|
||||
'#access' => ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin),
|
||||
'#weight' => -10,
|
||||
);
|
||||
|
||||
$form['account']['mail'] = array(
|
||||
|
@ -953,6 +954,35 @@ 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 if they logged in via a one-time login link.
|
||||
if (!$pass_reset) {
|
||||
$protected_values['mail'] = $form['account']['mail']['#title'];
|
||||
$protected_values['pass'] = t('Password');
|
||||
$request_new = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
|
||||
$current_pass_description = t('Enter your current password to change the %mail or %pass. !request_new.', array('%mail' => $protected_values['mail'], '%pass' => $protected_values['pass'], '!request_new' => $request_new));
|
||||
}
|
||||
// The user must enter their current password to change to a new one.
|
||||
if ($user->uid == $account->uid) {
|
||||
$form['account']['current_pass_required_values'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $protected_values,
|
||||
);
|
||||
$form['account']['current_pass'] = array(
|
||||
'#type' => 'password',
|
||||
'#title' => t('Current password'),
|
||||
'#size' => 25,
|
||||
'#access' => !empty($protected_values),
|
||||
'#description' => $current_pass_description,
|
||||
'#weight' => -5,
|
||||
);
|
||||
$form['#validate'][] = 'user_validate_current_pass';
|
||||
}
|
||||
}
|
||||
elseif (!variable_get('user_email_verification', TRUE) || $admin) {
|
||||
$form['account']['pass'] = array(
|
||||
|
@ -1048,6 +1078,30 @@ function user_account_form(&$form, &$form_state) {
|
|||
$form['#validate'][] = 'user_validate_picture';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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().
|
||||
*/
|
||||
|
@ -1439,7 +1493,7 @@ function user_menu() {
|
|||
'title' => 'Request new password',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('user_pass'),
|
||||
'access callback' => 'user_is_anonymous',
|
||||
'access callback' => TRUE,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'user.pages.inc',
|
||||
);
|
||||
|
@ -3204,7 +3258,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) {
|
||||
|
|
|
@ -29,6 +29,8 @@ function user_autocomplete($string = '') {
|
|||
* @see user_pass_submit()
|
||||
*/
|
||||
function user_pass() {
|
||||
global $user;
|
||||
|
||||
$form['name'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Username or e-mail address'),
|
||||
|
@ -36,6 +38,16 @@ function user_pass() {
|
|||
'#maxlength' => max(USERNAME_MAX_LENGTH, EMAIL_MAX_LENGTH),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
// Allow logged in users to request this also.
|
||||
if ($user->uid > 0) {
|
||||
$form['name']['#type'] = 'value';
|
||||
$form['name']['#value'] = $user->mail;
|
||||
$form['mail'] = array(
|
||||
'#prefix' => '<p>',
|
||||
'#markup' => t('Password reset instructions will be mailed to %email. You must log out to use the password reset link in the e-mail.', array('%email' => $user->mail)),
|
||||
'#suffix' => '</p>',
|
||||
);
|
||||
}
|
||||
$form['submit'] = array('#type' => 'submit', '#value' => t('E-mail new password'));
|
||||
|
||||
return $form;
|
||||
|
@ -120,7 +132,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 log in. 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('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $account->name, '%expiration_date' => format_date($timestamp + $timeout))));
|
||||
|
@ -219,7 +234,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 +297,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();
|
||||
|
||||
|
|
|
@ -1396,5 +1396,34 @@ 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 the error message appears when attempting to change the mail or
|
||||
// pass 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."));
|
||||
|
||||
// Test that the user must enter current password before changing passwords.
|
||||
$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'))));
|
||||
|
||||
// Try again with the current 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);
|
||||
$this->drupalLogout();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue