Heine

  • drupal
  • drupal core commits
  • about
Home

Commit 231590 by dries

- Patch #497612 by Moshe Weitzman et al: harden user login by correctly using the form API. Complete commit now. Thank you, thank you.

--- install.php 2009/06/08 04:33:35     1.178
+++ install.php 2009/06/30 11:32:08     1.179
@@ -1158,8 +1158,9 @@
   $account = user_load(1);
   $merge_data = array('init' => $form_state['values']['mail'], 'roles' => array(), 'status' => 1);
   user_save($account, array_merge($form_state['values'], $merge_data));
-  // Log in the first user.
-  user_authenticate($form_state['values']);
+  // Load global $user and perform final login tasks.
+  $form_state['uid'] = 1;
+  user_login_submit(array(), $form_state);
   $form_state['values'] = $form_state['old_values'];
   unset($form_state['old_values']);
   variable_set('user_email_verification', TRUE);
@@ -1167,9 +1168,6 @@
   if (isset($form_state['values']['clean_url'])) {
     variable_set('clean_url', $form_state['values']['clean_url']);
   }
-  // The user is now logged in, but has no session ID yet, which
-  // would be required later in the request, so remember it.
-  $user->sid = session_id();
 
   // Record when this install ran.
   variable_set('install_time', $_SERVER['REQUEST_TIME']);

--- modules/blogapi/blogapi.module      2009/06/12 08:39:35     1.155
+++ modules/blogapi/blogapi.module      2009/06/30 11:32:08     1.156
@@ -683,13 +683,14 @@
  * Ensure that the given user has permission to edit a blog.
  */
 function blogapi_validate_user($username, $password) {
-  global $user;
+  $form_state['values']['name'] = $username;
+  $form_state['values']['pass'] = $password;
+  $form_state['values']['op'] = t('Login');
+  drupal_form_submit('user_login', $form_state);
 
-  $user = user_authenticate(array('name' => $username, 'pass' => $password));
-
-  if ($user->uid) {
+  if (!form_get_error()) {
     if (user_access('administer content with blog api', $user)) {
-      return $user;
+      return $GLOBALS['user'];
     }
     else {
       return t('You do not have permission to edit this blog.');

--- modules/blogapi/blogapi.test        2009/06/12 13:59:56     1.12
+++ modules/blogapi/blogapi.test        2009/06/30 11:32:08     1.13
@@ -31,7 +31,6 @@
 
     // Create user.
     $web_user = $this->drupalCreateUser(array('create blog content', 'delete own blog content', 'edit own blog content', 'administer content with blog api'));
-    $this->drupalLogin($web_user);
 
     // Init common variables.
     $local = url($base_url . '/xmlrpc.php', array('external' => TRUE));

--- modules/openid/openid.module        2009/06/10 20:13:20     1.50
+++ modules/openid/openid.module        2009/06/30 11:32:08     1.51
@@ -409,7 +409,13 @@
   $account = user_external_load($identity);
   if (isset($account->uid)) {
     if (!variable_get('user_email_verification', TRUE) || $account->login) {
-      user_external_login($account, $_SESSION['openid']['user_login_values']);
+      // Check if user is blocked.
+      user_login_name_validate(array(), $state, (array)$account);
+      if (!form_get_errors()) {
+        // Load global $user and perform final login tasks.
+        $form_state['uid'] = $account->uid;
+        user_login_submit(array(), $form_state);
+      }
     }
     else {
       drupal_set_message(t('You must validate your email address for this account before logging in via OpenID'));
@@ -446,7 +452,9 @@
         drupal_goto();
       }
       user_set_authmaps($account, array("authname_openid" => $identity));
-      user_external_login($account);
+      // Load global $user and perform final login tasks.
+      $form_state['uid'] = $account->uid;
+      user_login_submit(array(), $form_state);
     }
     drupal_redirect_form($form, $form_state['redirect']);
   }

--- modules/simpletest/tests/session_test.module        2009/06/02 06:58:17     1.8
+++ modules/simpletest/tests/session_test.module        2009/06/30 11:32:08     1.9
@@ -122,7 +122,7 @@
  * Implement hook_user().
  */
 function session_test_user_login($edit = array(), $user = NULL) {
-  if ($edit['name'] == 'session_test_user') {
+  if ($user->name == 'session_test_user') {
     // Exit so we can verify that the session was regenerated
     // before hook_user() was called.
     exit;

--- modules/user/user.module    2009/06/28 12:52:57     1.1003
+++ modules/user/user.module    2009/06/30 11:32:08     1.1004
@@ -121,45 +121,6 @@
 }
 
 /**
- * Perform standard Drupal login operations for a user object.
- *
- * The user object must already be authenticated. This function verifies
- * that the user account is not blocked and then performs the login,
- * updates the login timestamp in the database, invokes hook_user('login'),
- * and regenerates the session.
- *
- * @param $account
- *    An authenticated user object to be set as the currently logged
- *    in user.
- * @param $edit
- *    The array of form values submitted by the user, if any.
- *    This array is passed to hook_user op login.
- * @return boolean
- *    TRUE if the login succeeds, FALSE otherwise.
- */
-function user_external_login($account, $edit = array()) {
-  $form = drupal_render(drupal_get_form('user_login'));
-
-  $state['values'] = $edit;
-  if (empty($state['values']['name'])) {
-    $state['values']['name'] = $account->name;
-  }
-
-  // Check if user is blocked.
-  user_login_name_validate($form, $state, (array)$account);
-  if (form_get_errors()) {
-    // Invalid login.
-    return FALSE;
-  }
-
-  // Valid login.
-  global $user;
-  $user = $account;
-  user_authenticate_finalize($state['values']);
-  return TRUE;
-}
-
-/**
  * Load multiple users based on certain conditions.
  *
  * This function should be used whenever you need to load more than one user
@@ -1614,7 +1575,8 @@
  * authentication fails. Distributed authentication modules are welcome
  * to use hook_form_alter() to change this series in order to
  * authenticate against their user database instead of the local users
- * table.
+ * table. If a distributed authentication module is successful, it
+ * should set $form_state['uid'] to a user ID.
  *
  * We use three validators instead of one since external authentication
  * modules usually only need to alter the second validator.
@@ -1641,10 +1603,11 @@
 
 /**
  * A validate handler on the login form. Check supplied username/password
- * against local users table. If successful, sets the global $user object.
+ * against local users table. If successful, $form_state['uid']
+ * is set to the matching user ID.
  */
 function user_login_authenticate_validate($form, &$form_state) {
-  user_authenticate($form_state['values']);
+  user_authenticate($form_state);
 }
 
 /**
@@ -1652,34 +1615,33 @@
  * error if user has not been authenticated yet.
  */
 function user_login_final_validate($form, &$form_state) {
-  global $user;
-  if (!$user->uid) {
+  if (empty($form_state['uid'])) {
     form_set_error('name', t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password'))));
     watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
   }
 }
 
 /**
- * Try to log in the user locally.
- *
- * @param $form_values
- *   Form values with at least 'name' and 'pass' keys, as well as anything else
- *   which should be passed along to hook_user op 'login'.
+ * Try to log in the user locally. $form_state['uid'] is set to
+ * a user ID if successful.
  *
- * @return
- *  A $user object, if successful.
+ * @param $form_state
+ *   Form submission state with at least 'name' and 'pass' keys.
  */
-function user_authenticate($form_values = array()) {
-  global $user;
-
-  $password = trim($form_values['pass']);
+function user_authenticate(&$form_state) {
+  $password = trim($form_state['values']['pass']);
   // Name and pass keys are required.
-  if (!empty($form_values['name']) && !empty($password)) {
-    $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_values['name']))->fetchObject();
+  if (!empty($form_state['values']['name']) && !empty($password)) {
+    $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_state['values']['name']))->fetchObject();
     if ($account) {
       // Allow alternate password hashing schemes.
       require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
       if (user_check_password($password, $account)) {
+        
+        // Successful authentication. Set a flag for user_login_final_validate().
+        $form_state['uid'] = $account->uid;
+        
+        // Update user to new password scheme if needed.
         if (user_needs_new_hash($account)) {
           $new_hash = user_hash_password($password);
           if ($new_hash) {
@@ -1689,10 +1651,6 @@
               ->execute();
           }
         }
-        $users = user_load_multiple(array($account->uid), array('status' => '1'));
-        $user = reset($users);
-        user_authenticate_finalize($form_values);
-        return $user;
       }
     }
   }
@@ -1702,12 +1660,9 @@
  * Finalize the login process. Must be called when logging in a user.
  *
  * The function records a watchdog message about the new session, saves the
- * login timestamp, calls hook_user op 'login' and generates a new session.
- *
- * $param $edit
- *   This array is passed to hook_user op login.
+ * login timestamp, calls hook_user op 'login' and generates a new session. *
  */
-function user_authenticate_finalize(&$edit) {
+function user_login_finalize(&$edit = array()) {
   global $user;
   watchdog('user', 'Session opened for %name.', array('%name' => $user->name));
   // Update the user table timestamp noting user has logged in.
@@ -1727,32 +1682,26 @@
 }
 
 /**
- * Submit handler for the login form. Redirects the user to a page.
- *
- * The user is redirected to the My Account page. Setting the destination in
- * the query string (as done by the user login block) overrides the redirect.
+ * Submit handler for the login form. Load $user object and perform standard login
+ * tasks. The user is then redirected to the My Account page. Setting the
+ * destination in the query string overrides the redirect.
  */
 function user_login_submit($form, &$form_state) {
   global $user;
-  if ($user->uid) {
-    $form_state['redirect'] = 'user/' . $user->uid;
-    return;
-  }
+  $user = user_load($form_state['uid']);
+  user_login_finalize();
+  
+  $form_state['redirect'] = 'user/' . $user->uid;
 }
 
 /**
- * Helper function for authentication modules. Either login in or registers
+ * Helper function for authentication modules. Either logs in or registers
  * the current user, based on username. Either way, the global $user object is
- * populated based on $name.
+ * populated and login tasks are performed.
  */
 function user_external_login_register($name, $module) {
-  global $user;
-
-  $existing_user = user_load_by_name($name);
-  if (isset($existing_user->uid)) {
-    $user = $existing_user;
-  }
-  else {
+  $account = user_load_by_name($name);
+  if (!$account->uid) {
     // Register this new user.
     $userinfo = array(
       'name' => $name,
@@ -1768,9 +1717,11 @@
       return;
     }
     user_set_authmaps($account, array("authname_$module" => $name));
-    $user = $account;
-    watchdog('user', 'New external user: %name using module %module.', array('%name' => $name, '%module' => $module), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $user->uid . '/edit'));
   }
+  
+  // Log user in.
+  $form_state['uid'] = $account->uid;
+  user_login_submit(array(), $form_state);
 }
 
 function user_pass_reset_url($account) {
@@ -2816,7 +2767,8 @@
       drupal_set_message(t('</p><p> Your password is <strong>%pass</strong>. You may change your password below.</p>', array('%pass' => $pass)));
     }
 
-    user_authenticate(array_merge($form_state['values'], $merge_data));
+    $form_state['values'] += $merge_data;
+    user_authenticate(array_merge($form_state));
 
     $form_state['redirect'] = 'user/1/edit';
     return;

--- modules/user/user.pages.inc 2009/06/30 11:30:27     1.43
+++ modules/user/user.pages.inc 2009/06/30 11:32:08     1.44
@@ -101,9 +101,9 @@
           watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $account->name, '%timestamp' => $timestamp));
           // Set the new user.
           $user = $account;
-          // user_authenticate_finalize() also updates the login timestamp of the
+          // user_login_finalize() also updates the login timestamp of the
           // user, which invalidates further use of the one-time login link.
-          user_authenticate_finalize($form_state['values']);
+          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');
         }

No votes yet
  • Drupal Core
  • Download patch

Recent posts

  • Menu access, a new pitfall when going back to Drupal 5
  • Drupal 6: $base_path doesn't always point to the frontpage
  • XNA SoundEffect ContentLoadException
  • A new form element in Drupal core
  • The backstabbing March-Hare

All-time popular content

  • Update UID 1 password via JS
  • Keeping an eye on Drupal core
  • Subversion on Strato V-PowerServer
  • A new form element in Drupal core
  • Access denied - Are you sure?
more

Tags

Captcha Coding Drupal FAPI IIS Let's hope it doesn't become popular Performance PHP Planet Drupal Quiz Security V-PowerServer
more tags
  • drupal
  • drupal core commits
  • about

Copyright © 2009 by Heine Deelstra. All rights reserved.