Index: includes/password.inc =================================================================== RCS file: includes/password.inc diff -N includes/password.inc --- includes/password.inc 26 Feb 2009 07:30:26 -0000 1.6 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,243 +0,0 @@ -> 6) & 0x3f]; - if ($i++ >= $count) { - break; - } - if ($i < $count) { - $value |= ord($input[$i]) << 16; - } - $output .= $itoa64[($value >> 12) & 0x3f]; - if ($i++ >= $count) { - break; - } - $output .= $itoa64[($value >> 18) & 0x3f]; - } while ($i < $count); - - return $output; -} - -/** - * Generates a random base 64-encoded salt prefixed with settings for the hash. - * - * Proper use of salts may defeat a number of attacks, including: - * - The ability to try candidate passwords against multiple hashes at once. - * - The ability to use pre-hashed lists of candidate passwords. - * - The ability to determine whether two users have the same (or different) - * password without actually having to guess one of the passwords. - * - * @param $count_log2 - * Integer that determines the number of iterations used in the hashing - * process. A larger value is more secure, but takes more time to complete. - * - * @return - * A 12 character string containing the iteration count and a random salt. - */ -function _password_generate_salt($count_log2) { - $output = '$P$'; - // Minimum log2 iterations is DRUPAL_MIN_HASH_COUNT. - $count_log2 = max($count_log2, DRUPAL_MIN_HASH_COUNT); - // Maximum log2 iterations is DRUPAL_MAX_HASH_COUNT. - // We encode the final log2 iteration count in base 64. - $itoa64 = _password_itoa64(); - $output .= $itoa64[min($count_log2, DRUPAL_MAX_HASH_COUNT)]; - // 6 bytes is the standard salt for a portable phpass hash. - $output .= _password_base64_encode(drupal_random_bytes(6), 6); - return $output; -} - -/** - * Hash a password using a secure stretched hash. - * - * By using a salt and repeated hashing the password is "stretched". Its - * security is increased because it becomes much more computationally costly - * for an attacker to try to break the hash by brute-force computation of the - * hashes of a large number of plain-text words or strings to find a match. - * - * @param $password - * The plain-text password to hash. - * @param $setting - * An existing hash or the output of _password_generate_salt(). - * - * @return - * A string containing the hashed password (and salt) or FALSE on failure. - */ -function _password_crypt($password, $setting) { - // The first 12 characters of an existing hash are its setting string. - $setting = substr($setting, 0, 12); - - if (substr($setting, 0, 3) != '$P$') { - return FALSE; - } - $count_log2 = _password_get_count_log2($setting); - // Hashes may be imported from elsewhere, so we allow != DRUPAL_HASH_COUNT - if ($count_log2 < DRUPAL_MIN_HASH_COUNT || $count_log2 > DRUPAL_MAX_HASH_COUNT) { - return FALSE; - } - $salt = substr($setting, 4, 8); - // Hashes must have an 8 character salt. - if (strlen($salt) != 8) { - return FALSE; - } - - // We must use md5() or sha1() here since they are the only cryptographic - // primitives always available in PHP 5. To implement our own low-level - // cryptographic function in PHP would result in much worse performance and - // consequently in lower iteration counts and hashes that are quicker to crack - // (by non-PHP code). - - $count = 1 << $count_log2; - - $hash = md5($salt . $password, TRUE); - do { - $hash = md5($hash . $password, TRUE); - } while (--$count); - - $output = $setting . _password_base64_encode($hash, 16); - // _password_base64_encode() of a 16 byte MD5 will always be 22 characters. - return (strlen($output) == 34) ? $output : FALSE; -} - -/** - * Parse the log2 iteration count from a stored hash or setting string. - */ -function _password_get_count_log2($setting) { - $itoa64 = _password_itoa64(); - return strpos($itoa64, $setting[3]); -} - -/** - * Hash a password using a secure hash. - * - * @param $password - * A plain-text password. - * @param $count_log2 - * Optional integer to specify the iteration count. Generally used only during - * mass operations where a value less than the default is needed for speed. - * - * @return - * A string containing the hashed password (and a salt), or FALSE on failure. - */ -function user_hash_password($password, $count_log2 = 0) { - if (empty($count_log2)) { - // Use the standard iteration count. - $count_log2 = variable_get('password_count_log2', DRUPAL_HASH_COUNT); - } - return _password_crypt($password, _password_generate_salt($count_log2)); -} - -/** - * Check whether a plain text password matches a stored hashed password. - * - * Alternative implementations of this function may use other data in the - * $account object, for example the uid to look up the hash in a custom table - * or remote database. - * - * @param $password - * A plain-text password - * @param $account - * A user object with at least the fields from the {users} table. - * - * @return - * TRUE or FALSE. - */ -function user_check_password($password, $account) { - if (substr($account->pass, 0, 3) == 'U$P') { - // This may be an updated password from user_update_7000(). Such hashes - // have 'U' added as the first character and need an extra md5(). - $stored_hash = substr($account->pass, 1); - $password = md5($password); - } - else { - $stored_hash = $account->pass; - } - $hash = _password_crypt($password, $stored_hash); - return ($hash && $stored_hash == $hash); -} - -/** - * Check whether a user's hashed password needs to be replaced with a new hash. - * - * This is typically called during the login process when the plain text - * password is available. A new hash is needed when the desired iteration count - * has changed through a change in the variable password_count_log2 or - * DRUPAL_HASH_COUNT or if the user's password hash was generated in an update - * like user_update_7000(). - * - * Alternative implementations of this function might use other criteria based - * on the fields in $account. - * - * @param $account - * A user object with at least the fields from the {users} table. - * - * @return - * TRUE or FALSE. - */ -function user_needs_new_hash($account) { - // Check whether this was an updated password. - if ((substr($account->pass, 0, 3) != '$P$') || (strlen($account->pass) != 34)) { - return TRUE; - } - // Check whether the iteration count used differs from the standard number. - return (_password_get_count_log2($account->pass) != variable_get('password_count_log2', DRUPAL_HASH_COUNT)); -} - Index: modules/dblog/dblog.test =================================================================== RCS file: /cvs/drupal/drupal/modules/dblog/dblog.test,v retrieving revision 1.28 diff -u -p -r1.28 dblog.test --- modules/dblog/dblog.test 22 Aug 2009 00:58:52 -0000 1.28 +++ modules/dblog/dblog.test 25 Aug 2009 00:25:11 -0000 @@ -182,7 +182,7 @@ class DBLogTestCase extends DrupalWebTes private function doUser() { // Set user variables. $name = $this->randomName(); - $pass = user_password(); + $pass = user_generate_password(); // Add user using form to generate add user event (which is not triggered by drupalCreateUser). $edit = array(); $edit['name'] = $name; Index: modules/openid/openid.module =================================================================== RCS file: /cvs/drupal/drupal/modules/openid/openid.module,v retrieving revision 1.56 diff -u -p -r1.56 openid.module --- modules/openid/openid.module 24 Aug 2009 00:14:21 -0000 1.56 +++ modules/openid/openid.module 25 Aug 2009 00:25:11 -0000 @@ -137,7 +137,7 @@ function openid_form_user_register_alter // with random password to avoid confusion. if (!variable_get('user_email_verification', TRUE)) { $form['pass']['#type'] = 'hidden'; - $form['pass']['#value'] = user_password(); + $form['pass']['#value'] = user_generate_password(); } $form['auth_openid'] = array('#type' => 'hidden', '#value' => $_SESSION['openid']['values']['auth_openid']); } @@ -430,7 +430,7 @@ function openid_authentication($response $form_state['redirect'] = NULL; $form_state['values']['name'] = (empty($response['openid.sreg.nickname'])) ? $identity : $response['openid.sreg.nickname']; $form_state['values']['mail'] = (empty($response['openid.sreg.email'])) ? '' : $response['openid.sreg.email']; - $form_state['values']['pass'] = user_password(); + $form_state['values']['pass'] = user_generate_password(); $form_state['values']['status'] = variable_get('user_register', 1) == 1; $form_state['values']['response'] = $response; $form = drupal_retrieve_form('user_register', $form_state); Index: modules/simpletest/drupal_web_test_case.php =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v retrieving revision 1.144 diff -u -p -r1.144 drupal_web_test_case.php --- modules/simpletest/drupal_web_test_case.php 24 Aug 2009 00:14:21 -0000 1.144 +++ modules/simpletest/drupal_web_test_case.php 25 Aug 2009 00:25:11 -0000 @@ -861,7 +861,7 @@ class DrupalWebTestCase extends DrupalTe $edit['name'] = $this->randomName(); $edit['mail'] = $edit['name'] . '@example.com'; $edit['roles'] = array($rid => $rid); - $edit['pass'] = user_password(); + $edit['pass'] = user_generate_password(); $edit['status'] = 1; $account = user_save('', $edit); Index: modules/user/user.info =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.info,v retrieving revision 1.12 diff -u -p -r1.12 user.info --- modules/user/user.info 19 Aug 2009 20:19:37 -0000 1.12 +++ modules/user/user.info 25 Aug 2009 00:25:12 -0000 @@ -8,6 +8,7 @@ files[] = user.module files[] = user.admin.inc files[] = user.pages.inc files[] = user.install +files[] = user.password.inc files[] = user.test files[] = user.tokens.inc required = TRUE Index: modules/user/user.install =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.install,v retrieving revision 1.28 diff -u -p -r1.28 user.install --- modules/user/user.install 21 Aug 2009 14:28:52 -0000 1.28 +++ modules/user/user.install 25 Aug 2009 00:25:12 -0000 @@ -256,8 +256,7 @@ function user_schema() { */ function user_update_7000(&$sandbox) { $ret = array('#finished' => 0); - // Lower than DRUPAL_HASH_COUNT to make the update run at a reasonable speed. - $hash_count_log2 = 11; + // Multi-part update. if (!isset($sandbox['user_from'])) { db_change_field($ret, 'users', 'pass', 'pass', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => '')); @@ -265,15 +264,16 @@ function user_update_7000(&$sandbox) { $sandbox['user_count'] = db_query("SELECT COUNT(uid) FROM {users}")->fetchField(); } else { - require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); - // Hash again all current hashed passwords. + // Hash again all current hashed passwords at lower than the default + // strength to make the update run at a better speed. + user_authenticator()->setPasswordStorageStrength(0.1); $has_rows = FALSE; // Update this many per page load. $count = 1000; $result = db_query_range("SELECT uid, pass FROM {users} WHERE uid > 0 ORDER BY uid", array(), $sandbox['user_from'], $count); foreach ($result as $account) { $has_rows = TRUE; - $new_hash = user_hash_password($account->pass, $hash_count_log2); + $new_hash = user_authenticator()->hashPassword($account['pass']); if ($new_hash) { // Indicate an updated password. $new_hash = 'U' . $new_hash; @@ -287,7 +287,7 @@ function user_update_7000(&$sandbox) { $sandbox['user_from'] += $count; if (!$has_rows) { $ret['#finished'] = 1; - $ret[] = array('success' => TRUE, 'query' => "UPDATE {users} SET pass = 'U' . user_hash_password(pass) WHERE uid > 0"); + $ret[] = array('success' => TRUE, 'query' => "UPDATE {users} SET pass = 'U' . user_authenticator()->hashPassword(pass) WHERE uid > 0"); } } return $ret; Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.1030 diff -u -p -r1.1030 user.module --- modules/user/user.module 24 Aug 2009 00:14:23 -0000 1.1030 +++ modules/user/user.module 25 Aug 2009 00:25:12 -0000 @@ -375,19 +375,15 @@ function user_save($account, $edit = arr $user_fields = $table['fields']; if (!empty($edit['pass'])) { - // Allow alternate password hashing schemes. - require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); - $edit['pass'] = user_hash_password(trim($edit['pass'])); - // Abort if the hashing failed and returned FALSE. - if (!$edit['pass']) { - return FALSE; - } + // Retain the raw password to call the update method later. + $raw_password = trim($edit['pass']); + // Generate a hashed password to be saved into the database. + $edit['pass'] = user_authenticator()->hashPassword($raw_password); } else { // Avoid overwriting an existing password with a blank password. unset($edit['pass']); } - // Get the fields form so we can recognize the fields in the $edit // form that should not go into the serialized data array. $field_form = array(); @@ -411,6 +407,7 @@ function user_save($account, $edit = arr if (empty($edit['access']) && empty($account->access) && user_access('administer users')) { $edit['access'] = REQUEST_TIME; } + foreach ($edit as $key => $value) { // Form fields that don't pertain to the users, user_roles, or // Field API are automatically serialized into the users.data @@ -492,15 +489,6 @@ function user_save($account, $edit = arr drupal_session_destroy_uid($account->uid); } - // If the password changed, delete all open sessions and recreate - // the current one. - if (!empty($edit['pass'])) { - drupal_session_destroy_uid($account->uid); - if ($account->uid == $GLOBALS['user']->uid) { - drupal_session_regenerate(); - } - } - // Save Field data. $object = (object) $edit; field_attach_update('user', $object); @@ -508,6 +496,15 @@ function user_save($account, $edit = arr // Refresh user object. $user = user_load($account->uid, TRUE); + // If the password changed, delete all open sessions and recreate + // the current one. + if (!empty($raw_password)) { + user_authenticator()->updatePassword($raw_password, $user); + drupal_session_destroy_uid($account->uid); + if ($account->uid == $GLOBALS['user']->uid) { + drupal_session_regenerate(); + } + } // Send emails after we have the new user object. if (isset($edit['status']) && $edit['status'] != $account->status) { // The user's status is changing; conditionally send notification email. @@ -579,6 +576,8 @@ function user_save($account, $edit = arr // Build the finished user object. $user = user_load($edit['uid'], TRUE); + // Update the password using the final user object. + user_authenticator()->updatePassword($raw_password, $user); } return $user; @@ -651,7 +650,7 @@ function user_validate_picture(&$form, & /** * Generate a random alphanumeric password. */ -function user_password($length = 10) { +function user_generate_password($length = 10) { // This variable contains the list of allowable characters for the // password. Note that the number 0 and the letter 'O' have been // removed to avoid confusion between the two. The same is true @@ -1775,22 +1774,9 @@ function user_authenticate($name, $passw if (!empty($name) && !empty($password)) { $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $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)) { + if (user_authenticator()->checkPassword($password, $account)) { // Successful authentication. $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) { - db_update('users') - ->fields(array('pass' => $new_hash)) - ->condition('uid', $account->uid) - ->execute(); - } - } } } } @@ -1846,7 +1832,7 @@ function user_external_login_register($n // Register this new user. $userinfo = array( 'name' => $name, - 'pass' => user_password(), + 'pass' => user_generate_password(), 'init' => $name, 'status' => 1, 'access' => REQUEST_TIME @@ -2879,7 +2865,7 @@ function user_register_submit($form, &$f $pass = $form_state['values']['pass']; } else { - $pass = user_password(); + $pass = user_generate_password(); }; $notify = isset($form_state['values']['notify']) ? $form_state['values']['notify'] : NULL; $from = variable_get('site_mail', ini_get('sendmail_from')); @@ -3039,6 +3025,81 @@ function _user_forms(&$edit, $account, $ return empty($groups) ? FALSE : $groups; } +/** + * Returns an object that implements UserAuthenticationInterface. + */ +function user_authenticator() { + $instance = &drupal_static(__FUNCTION__); + + if (empty($instance)) { + $class = variable_get('authentication_system', 'UserPassword'); + $instance = new $class; + // Set a default password storage (hash) strength. + $instance->setPasswordStorageStrength(variable_get('password_storage_strength', 1.0)); + } + return $instance; +} + +/** + * An interface for authenticating users with passwords, and for updating passwords. + */ +interface UserAuthenticationInterface { + /** + * Set the relative strength for password storage as compared to the default. + * + * This method may have no effect if the passwords are stored remotely or + * using a fixed hashing or cryptographic algorithm. + * + * @param $strength + * A positive number; 1.0 for the default. Typical range is 0.01 - 1000.0. + */ + public function setPasswordStorageStrength($strength); + + /** + * Hash a password using a secure hash. + * + * This method is used when saving a user to the database. If the + * implementation does not wish to return its hash value a non-empty string + * with a fixed value (e.g. the class name) must be returned. + * + * @param $password + * A plain-text password. + * + * @return + * A string containing the hashed password, or FALSE on failure. + */ + public function hashPassword($password); + + /** + * Check whether a plain text password matches the user's stored password. + * + * @param $password + * A plain-text password + * @param $account + * A user object with at least the fields from the {users} table. + * + * @return + * TRUE if it matches or FALSE otherwise. + */ + public function checkPassword($password, $account); + + /** + * Update a user's password with a new password. + * + * This method may have no effect if the passwords are stored remotely or + * managed outside of Drupal. If updates are not possible or the operation + * fails, the method must return FALSE. + * + * @param $password + * A plain-text password + * @param $account + * A user object with at least the fields from the {users} table. + * + * @return + * TRUE or FALSE. + */ + public function updatePassword($password, $account); +} /** * Implementation of hook_modules_installed(). Index: modules/user/user.password.inc =================================================================== RCS file: modules/user/user.password.inc diff -N modules/user/user.password.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/user/user.password.inc 25 Aug 2009 00:25:12 -0000 @@ -0,0 +1,269 @@ +itoa64[$value & 0x3f]; + if ($i < $count) { + $value |= ord($input[$i]) << 8; + } + $output .= $this->itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) { + break; + } + if ($i < $count) { + $value |= ord($input[$i]) << 16; + } + $output .= $this->itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) { + break; + } + $output .= $this->itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + /** + * Generates a random base 64-encoded salt prefixed with settings for the hash. + * + * Proper use of salts may defeat a number of attacks, including: + * - The ability to try candidate passwords against multiple hashes at once. + * - The ability to use pre-hashed lists of candidate passwords. + * - The ability to determine whether two users have the same (or different) + * password without actually having to guess one of the passwords. + * + * @return + * A 12 character string containing the iteration count and a random salt. + */ + protected function generateSalt() { + // $P$ is the hash identifier for phpass. + $output = '$P$'; + // Minimum log2 iterations is DRUPAL_MIN_HASH_COUNT. + $count_log2 = max($this->countLog2Setting, self::MIN_HASH_COUNT); + // Maximum log2 iterations is DRUPAL_MAX_HASH_COUNT. + // We encode the final log2 iteration count in base 64. + $output .= $this->itoa64[min($count_log2, self::MAX_HASH_COUNT)]; + // 6 bytes is the standard salt for a portable phpass hash. + $output .= $this->base64Encode(drupal_random_bytes(6), 6); + return $output; + } + + /** + * Hash a password using a secure stretched hash. + * + * By using a salt and repeated hashing the password is "stretched". Its + * security is increased because it becomes much more computationally costly + * for an attacker to try to break the hash by brute-force computation of the + * hashes of a large number of plain-text words or strings to find a match. + * + * @param $password + * The plain-text password to hash. + * @param $setting + * An existing hash or the output of generate_salt(). + * + * @return + * A string containing the hashed password (and salt) or FALSE on failure. + */ + protected function passwordCrypt($password, $setting) { + // The first 12 characters of an existing hash are its setting string. + $setting = substr($setting, 0, 12); + + if (substr($setting, 0, 3) != '$P$') { + return FALSE; + } + $count_log2 = $this->getCountLog2($setting); + // Hashes may be imported from elsewhere, so we allow != DEFAULT_HASH_COUNT + if ($count_log2 < self::MIN_HASH_COUNT || $count_log2 > self::MAX_HASH_COUNT) { + return FALSE; + } + $salt = substr($setting, 4, 8); + // Hashes must have an 8 character salt. + if (strlen($salt) != 8) { + return FALSE; + } + + // We must use md5() or sha1() here since they are the only cryptographic + // primitives always available in PHP 5. To implement our own low-level + // cryptographic function in PHP would result in much worse performance and + // consequently in lower iteration counts and hashes that are quicker to crack + // (by non-PHP code). + + $count = 1 << $count_log2; + + $hash = md5($salt . $password, TRUE); + do { + $hash = md5($hash . $password, TRUE); + } while (--$count); + + $output = $setting . $this->base64Encode($hash, 16); + // this->base64Encode() of a 16 byte MD5 will always be 22 characters, plus + // 12 characters of base64 salt is 34 bytes total. + return (strlen($output) == 34) ? $output : FALSE; + } + + /** + * Parse the log2 iteration count from a stored hash or setting string. + */ + protected function getCountLog2($setting) { + return strpos($this->itoa64, $setting[3]); + } + + /** + * Set the relative strength for password storage as compared to the default. + * + * @param $strength + * A positive number; 1.0 for the default. Typical range is 0.01 - 1000.0. + */ + public function setPasswordStorageStrength($strength) { + // We accept a linear scale strength and convert it to a base 2 logarithm. + $this->countLog2Setting = (int)(log($strength, 2) + self::DEFAULT_HASH_COUNT); + } + + /** + * Hash a password using a secure hash. + * + * @param $password + * A plain-text password string. + * + * @return + * A string containing the hashed password (and a salt). + */ + public function hashPassword($password) { + $hash = $this->passwordCrypt($password, $this->generateSalt()); + if ($hash === FALSE) { + throw new Exception('Unable to generate a hash.'); + } + return $hash; + } + + /** + * Check whether a plain text password matches a stored hashed password. + * + * @param $password + * A plain-text password + * @param $account + * A user object with at least the fields from the {users} table. + * + * @return + * TRUE or FALSE. + */ + public function checkPassword($password, $account) { + $raw_password = $password; + if (substr($account->pass, 0, 3) == 'U$P') { + // This may be an updated password from user_update_7000(). Such hashes + // have 'U' added as the first character and need an extra md5(). + $stored_hash = substr($account->pass, 1); + $password = md5($password); + } + else { + $stored_hash = $account->pass; + } + $hash = $this->passwordCrypt($password, $stored_hash); + if ($hash && $stored_hash == $hash) { + if ($this->getCountLog2($account->pass) != $this->countLog2Setting) { + $this->updatePassword($raw_password, $account); + } + return TRUE; + } + return FALSE; + } + + /** + * Update a user's password with a new password. + * + * @param $password + * A plain-text password + * @param $account + * A user object with at least the fields from the {users} table. + * + * @return + * TRUE on success or FALSE otherwise. + */ + public function updatePassword($password, $account) { + $new_hash = $this->passwordCrypt($password, $this->generateSalt()); + if ($new_hash) { + db_update('users') + ->fields(array('pass' => $new_hash)) + ->condition('uid', $account->uid) + ->execute(); + return TRUE; + } + return FALSE; + } + + /** + * Update a user's password hash if it needs to be replaced with a new hash. + */ + protected function updateHash($password, $account) { + $update = FALSE; + // Check whether this was an updated password. + if ((substr($account->pass, 0, 3) != '$P$') || (strlen($account->pass) != 34)) { + $update = TRUE; + } + // Check whether the iteration count used differs from the standard number. + if ($this->getCountLog2($account->pass) != $this->countLog2Setting) { + $update = TRUE; + } + if ($update) { + $new_hash = $this->updatePassword($password, $account); + } + } +} + Index: modules/user/user.test =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.test,v retrieving revision 1.56 diff -u -p -r1.56 user.test --- modules/user/user.test 21 Aug 2009 14:27:47 -0000 1.56 +++ modules/user/user.test 25 Aug 2009 00:25:12 -0000 @@ -31,22 +31,22 @@ class UserRegistrationTestCase extends D $this->assertText(t('Your password and further instructions have been sent to your e-mail address.'), t('User registered successfully.')); // Check database for created user. - $users = user_load_multiple(array(), array('name' => $name, 'mail' => $mail)); - $user = reset($users); - $this->assertTrue($user, t('User found in database.')); - $this->assertTrue($user->uid > 0, t('User has valid user id.')); + $accounts = user_load_multiple(array(), array('name' => $name, 'mail' => $mail)); + $account = reset($accounts); + $this->assertTrue($account, t('User found in database.')); + $this->assertTrue($account->uid > 0, t('User has valid user id.')); // Check user fields. - $this->assertEqual($user->name, $name, t('Username matches.')); - $this->assertEqual($user->mail, $mail, t('E-mail address matches.')); - $this->assertEqual($user->theme, '', t('Correct theme field.')); - $this->assertEqual($user->signature, '', t('Correct signature field.')); - $this->assertTrue(($user->created > REQUEST_TIME - 20 ), t('Correct creation time.')); - $this->assertEqual($user->status, variable_get('user_register', 1) == 1 ? 1 : 0, t('Correct status field.')); - $this->assertEqual($user->timezone, variable_get('date_default_timezone'), t('Correct time zone field.')); - $this->assertEqual($user->language, '', t('Correct language field.')); - $this->assertEqual($user->picture, '', t('Correct picture field.')); - $this->assertEqual($user->init, $mail, t('Correct init field.')); + $this->assertEqual($account->name, $name, t('Username matches.')); + $this->assertEqual($account->mail, $mail, t('E-mail address matches.')); + $this->assertEqual($account->theme, '', t('Correct theme field.')); + $this->assertEqual($account->signature, '', t('Correct signature field.')); + $this->assertTrue(($account->created > REQUEST_TIME - 20 ), t('Correct creation time.')); + $this->assertEqual($account->status, variable_get('user_register', 1) == 1 ? 1 : 0, t('Correct status field.')); + $this->assertEqual($account->timezone, variable_get('date_default_timezone'), t('Correct time zone field.')); + $this->assertEqual($account->language, '', t('Correct language field.')); + $this->assertEqual($account->picture, '', t('Correct picture field.')); + $this->assertEqual($account->init, $mail, t('Correct init field.')); // Attempt to login with incorrect password. $edit = array(); @@ -56,7 +56,7 @@ class UserRegistrationTestCase extends D $this->assertText(t('Sorry, unrecognized username or password. Have you forgotten your password?'), t('Invalid login attempt failed.')); // Login using password reset page. - $url = user_pass_reset_url($user); + $url = user_pass_reset_url($account); $this->drupalGet($url); $this->assertText(t('This login can be used only once.'), t('Login can be used only once.')); @@ -72,36 +72,33 @@ class UserRegistrationTestCase extends D $this->assertNoText(t('The changes have been saved.'), t('Save user password with mismatched type in password confirm.')); // Change user password. - $new_pass = user_password(); + $new_pass = user_generate_password(); $edit = array(); $edit['pass[pass1]'] = $new_pass; $edit['pass[pass2]'] = $new_pass; $this->drupalPost(NULL, $edit, t('Save')); $this->assertText(t('The changes have been saved.'), t('Password changed to @password', array('@password' => $new_pass))); - // Make sure password changes are present in database. - require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); - - $user = user_load($user->uid, TRUE); - $this->assertTrue(user_check_password($new_pass, $user), t('Correct password in database.')); + $account = user_load($account->uid, TRUE); + $this->assertTrue(user_authenticator()->checkPassword($new_pass, $account), t('Correct password in database.')); // Logout of user account. $this->clickLink(t('Log out')); - $this->assertNoText($user->name, t('Logged out.')); + $this->assertNoText($account->name, t('Logged out.')); // Login user. $edit = array(); - $edit['name'] = $user->name; + $edit['name'] = $account->name; $edit['pass'] = $new_pass; $this->drupalPost('user', $edit, t('Log in')); $this->assertText(t('Log out'), t('Logged in.')); - $this->assertText($user->name, t('[logged in] Username found.')); + $this->assertText($account->name, t('[logged in] Username found.')); $this->assertNoText(t('Sorry. Unrecognized username or password.'), t('[logged in] No message for unrecognized username or password.')); $this->assertNoText(t('User login'), t('[logged in] No user login form present.')); $this->drupalGet('user'); - $this->assertText($user->name, t('[user auth] Not login page.')); + $this->assertText($account->name, t('[user auth] Not login page.')); $this->assertText(t('View'), t('[user auth] Found view tab on the profile page.')); $this->assertText(t('Edit'), t('[user auth] Found edit tab on the profile page.')); } Index: scripts/password-hash.sh =================================================================== RCS file: /cvs/drupal/drupal/scripts/password-hash.sh,v retrieving revision 1.6 diff -u -p -r1.6 password-hash.sh --- scripts/password-hash.sh 26 Feb 2009 07:30:29 -0000 1.6 +++ scripts/password-hash.sh 25 Aug 2009 00:25:12 -0000 @@ -85,11 +85,12 @@ while ($param = array_shift($_SERVER['ar define('DRUPAL_ROOT', getcwd()); -include_once DRUPAL_ROOT . '/includes/password.inc'; +include_once DRUPAL_ROOT . '/modules/user/user.module'; +include_once DRUPAL_ROOT . '/modules/user/user.password.inc'; include_once DRUPAL_ROOT . '/includes/common.inc'; foreach ($passwords as $password) { - print("\npassword: $password \t\thash: ". user_hash_password($password) ."\n"); + print("\npassword: $password \t\thash: ". user_authenticator()->hashPassword($password) ."\n"); } print("\n");