Add support for mysql versions >=4.0x by adding multiple hash attempts when logging in. We now
attempt sha1(), password() and if mysql >= 4.0x, old_password() hashes. Switch the default user hash from mysql's password() function to a sha1() hash. Update user::login() to flag logins with password() and old_password() hashes. If the flag is set we call user::update_password() to update the users password hash to one generated from the sha1 of the users password. Add a unit test to test logging in and upgrading from the old hashes.
This commit is contained in:
@@ -63,11 +63,55 @@ class User {
|
|||||||
{
|
{
|
||||||
$sQuery = "SELECT *
|
$sQuery = "SELECT *
|
||||||
FROM user_list
|
FROM user_list
|
||||||
WHERE email = '?'
|
WHERE email = '?' AND password = ";
|
||||||
AND password = password('?')";
|
|
||||||
$hResult = query_parameters($sQuery, $sEmail, $sPassword);
|
$sMysqlSHAPasswordPart = "SHA1('?');";
|
||||||
|
$sMysqlPasswordPart = "password('?');";
|
||||||
|
$sMysql40xPasswordPart = "old_password('?');";
|
||||||
|
|
||||||
|
// if true we used an old style password and we need to
|
||||||
|
// update the users password to the new style
|
||||||
|
$bUsedOldStylePassword = false;
|
||||||
|
|
||||||
|
$oRow = null; // null out the row object
|
||||||
|
|
||||||
|
// if we aren't logged in yet
|
||||||
|
// try to login with the mysql sha1() value of the password
|
||||||
|
if(!$oRow)
|
||||||
|
{
|
||||||
|
$hResult = query_parameters($sQuery.$sMysqlSHAPasswordPart,
|
||||||
|
$sEmail, $sPassword);
|
||||||
|
$oRow = mysql_fetch_object($hResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we aren't logged in yet
|
||||||
|
// try to login with the mysql password() value of the password
|
||||||
|
if(!$oRow)
|
||||||
|
{
|
||||||
|
$hResult = query_parameters($sQuery.$sMysqlPasswordPart,
|
||||||
|
$sEmail, $sPassword);
|
||||||
|
$oRow = mysql_fetch_object($hResult);
|
||||||
|
if($oRow) $bUsedOldStylePassword = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we aren't logged in yet
|
||||||
|
// try to login with the mysql old_password() value of the password
|
||||||
|
if(!$oRow)
|
||||||
|
{
|
||||||
|
// make sure we have a newer version, older versions may not
|
||||||
|
$sResult = mysql_get_server_info();
|
||||||
|
$fVersion = substr($sResult, 0, 3);
|
||||||
|
|
||||||
|
// if we have a newer version of mysql, try with the 'old_password()' function
|
||||||
|
if($fVersion >= 4.1)
|
||||||
|
{
|
||||||
|
$hResult = query_parameters($sQuery.$sMysql40xPasswordPart,
|
||||||
|
$sEmail, $sPassword);
|
||||||
|
$oRow = mysql_fetch_object($hResult);
|
||||||
|
if($oRow) $bUsedOldStylePassword = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$oRow = mysql_fetch_object($hResult);
|
|
||||||
if($oRow)
|
if($oRow)
|
||||||
{
|
{
|
||||||
$this->iUserId = $oRow->userid;
|
$this->iUserId = $oRow->userid;
|
||||||
@@ -76,6 +120,12 @@ class User {
|
|||||||
$this->sStamp = $oRow->stamp;
|
$this->sStamp = $oRow->stamp;
|
||||||
$this->sDateCreated = $oRow->created;
|
$this->sDateCreated = $oRow->created;
|
||||||
$this->sWineRelease = $oRow->CVSrelease;
|
$this->sWineRelease = $oRow->CVSrelease;
|
||||||
|
|
||||||
|
// if we used an old style password, update the users password
|
||||||
|
if($bUsedOldStylePassword)
|
||||||
|
{
|
||||||
|
$this->update_password($sPassword);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->isLoggedIn())
|
if($this->isLoggedIn())
|
||||||
@@ -115,7 +165,7 @@ class User {
|
|||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
$hResult = query_parameters("INSERT INTO user_list (realname, email, CVSrelease, password, stamp,".
|
$hResult = query_parameters("INSERT INTO user_list (realname, email, CVSrelease, password, stamp,".
|
||||||
"created) VALUES ('?', '?', '?', password('?'), ?, ?)",
|
"created) VALUES ('?', '?', '?', SHA1('?'), ?, ?)",
|
||||||
$sRealname, $sEmail, $sWineRelease, $sPassword, "NOW()", "NOW()");
|
$sRealname, $sEmail, $sWineRelease, $sPassword, "NOW()", "NOW()");
|
||||||
|
|
||||||
if(!$hResult) return USER_CREATE_FAILED;
|
if(!$hResult) return USER_CREATE_FAILED;
|
||||||
@@ -180,7 +230,8 @@ class User {
|
|||||||
{
|
{
|
||||||
if($sPassword)
|
if($sPassword)
|
||||||
{
|
{
|
||||||
if (query_parameters("UPDATE user_list SET password = password('?') WHERE userid = '?'",
|
if (query_parameters("UPDATE user_list SET password = SHA1('?') ".
|
||||||
|
"WHERE userid = '?'",
|
||||||
$sPassword, $this->iUserId))
|
$sPassword, $this->iUserId))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -319,12 +319,15 @@ if(!test_user_update_password())
|
|||||||
echo "test_user_update_password() passed\n";
|
echo "test_user_update_password() passed\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// perform tests related to user password migration
|
||||||
|
include_once("test_user_password_migration.php");
|
||||||
|
|
||||||
/* Perform the maintainer tests here because they require that a user we can log into */
|
/* Perform the maintainer tests here because they require that a user we can log into */
|
||||||
/* and we want to save on having to clean up the user by duplicating the cleanup code below */
|
/* and we want to save on having to clean up the user by duplicating the cleanup code below */
|
||||||
include_once("test_maintainer.php");
|
include_once("test_maintainer.php");
|
||||||
|
|
||||||
/* TODO: the rest of the user member functions we don't currently test */
|
|
||||||
|
|
||||||
|
/* TODO: the rest of the user member functions we don't currently test */
|
||||||
|
|
||||||
/* clean up the user we created during the tests */
|
/* clean up the user we created during the tests */
|
||||||
/* so the unit test leaves no trace that it ran */
|
/* so the unit test leaves no trace that it ran */
|
||||||
|
|||||||
85
unit_test/test_user_password_migration.php
Normal file
85
unit_test/test_user_password_migration.php
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// perform tests to verify that users can login with each of the
|
||||||
|
// possible hashing algorithms and that upon logging in a users
|
||||||
|
// passsword will be upgraded to the latest hashing scheme
|
||||||
|
|
||||||
|
function test_user_password_migration()
|
||||||
|
{
|
||||||
|
test_start(__FUNCTION__);
|
||||||
|
|
||||||
|
$bSuccess = true;
|
||||||
|
|
||||||
|
$sTestEmail = "user_password_migration@localhost.com";
|
||||||
|
$sTestPassword = "password";
|
||||||
|
|
||||||
|
if(!($oUser = create_and_login_user($sTestEmail, $sTestPassword)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// generate the SHA1() of the users password
|
||||||
|
$sQuery = "select SHA1('?') as password;";
|
||||||
|
$hResult = query_parameters($sQuery, $sTestPassword);
|
||||||
|
$oRow = mysql_fetch_object($hResult);
|
||||||
|
$sTestUserPasswordSHA1 = $oRow->password;
|
||||||
|
|
||||||
|
// test that the user was created with the sha1 hash of their password
|
||||||
|
$sQuery = "select password from user_list where userid = '?';";
|
||||||
|
$hResult = query_parameters($sQuery, $oUser->iUserId);
|
||||||
|
$oRow = mysql_fetch_object($hResult);
|
||||||
|
if($sTestUserPasswordSHA1 != $oRow->password)
|
||||||
|
{
|
||||||
|
error("sTestUserPasswordSHA1 $sTestUserPasswordSHA1 doesn't match oRow->password of $oRow->password after user::create()");
|
||||||
|
$bSuccess = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build an array of the different types of password hashing
|
||||||
|
$aPasswordForm = array();
|
||||||
|
$aPasswordForm[] = "old_password('?')";
|
||||||
|
$aPasswordForm[] = "password('?')";
|
||||||
|
$aPasswordForm[] = "sha1('?')";
|
||||||
|
|
||||||
|
foreach($aPasswordForm as $sPasswordForm)
|
||||||
|
{
|
||||||
|
// manually set the users password
|
||||||
|
$sQuery = "update user_list set password = ".$sPasswordForm." where userid = '?';";
|
||||||
|
query_parameters($sQuery, $sTestPassword, $oUser->iUserId);
|
||||||
|
|
||||||
|
// attempt to login
|
||||||
|
$retval = $oUser->login($sTestEmail, $sTestPassword);
|
||||||
|
if($retval != SUCCESS)
|
||||||
|
{
|
||||||
|
error("Failed to login when the user has an $sPasswordForm generated hash!");
|
||||||
|
$bSuccess = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that the users password has been updated to the SHA1 hash
|
||||||
|
// after the user was logged in
|
||||||
|
$sQuery = "select password from user_list where userid = '?';";
|
||||||
|
$hResult = query_parameters($sQuery, $oUser->iUserId);
|
||||||
|
$oRow = mysql_fetch_object($hResult);
|
||||||
|
if($sTestUserPasswordSHA1 != $oRow->password)
|
||||||
|
{
|
||||||
|
error("sTestUserPasswordSHA1 $sTestUserPasswordSHA1 doesn't match oRow->password of $oRow->password");
|
||||||
|
$bSuccess = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the user we created, we want the database to be left
|
||||||
|
// as it was before we ran our tests on it
|
||||||
|
$oUser->delete();
|
||||||
|
|
||||||
|
return $bSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!test_user_password_migration())
|
||||||
|
{
|
||||||
|
echo "test_user_password_migration() failed!\n";
|
||||||
|
$bTestSuccess = false;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
echo "test_user_password_migration() passed\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
Reference in New Issue
Block a user