Article
The PHP Anthology Volume 2, Chapter 1 - Access Control
How do I let users change their passwords?
You're now able to deal with people who have forgotten their passwords. What about people who want to change their passwords?
A good "test of design" for many PHP applications is whether users can change their passwords without needing to log back into the application afterwards. It's important to be considerate to your site's users if you want them to stick around. Changing their passwords should not require users to log back in, provided you construct your application carefully.
Going back to the session-based authentication mechanism you saw earlier in this chapter, you'll remember that the login and password are stored in session variables and rechecked on every new page by the Auth class. The trick is to change the value of the password in both the session variable and the database when users change their passwords.
First things first! Let's build a new login page using QuickForm:
Example 1.41. 11.php
<?php
// Include QuickForm class
require_once 'HTML/QuickForm.php';
// If $_GET['from'] comes from the Auth class
if (isset($_GET['from'])) {
$target = $_GET['from'];
} else {
// Default URL: usually index.php
$target = '12.php';
}
// Instantiate the QuickForm class
$form = new HTML_QuickForm('loginForm', 'POST', $target);
// Add a header to the form
$form->addHeader('Please Login');
// Add a field for the login name
$form->addElement('text', 'login', 'Username');
$form->addRule('login', 'Enter your login', 'required', FALSE,
'client');
// Add a field for the password
$form->addElement('password', 'password', 'Password');
$form->addRule('password', 'Enter your password', 'required',
FALSE, 'client');
// Add a submit button
$form->addElement('submit', 'submit', ' Login ');
$form->display();
?>
Note that in this case, we tell QuickForm to direct the submission of the form to another PHP script, rather than handling it locally on the same page.
As we've already been working on an AccountMaintenance class, this seems like a reasonable place to add the code that allows users to change their passwords:
Example 1.42. AccessControl/AccountMaintenance.php (in SPLIB) (excerpt)
/**
* Changes a password both in the database
* and in the current session variable.
* Assumes the new password has been
* validated correctly elsewhere.
* @param Auth instance of the Auth class
* @param string old password
* @param string new password
* @return boolean TRUE on success
* @access public
*/
function changePassword(&$auth, $oldPassword, $newPassword)
{
$oldPassword = mysql_escape_string($oldPassword);
$newPassword = mysql_escape_string($newPassword);
// Instantiate the Session class
$session = new Session();
// Check the the login and old password match
$sql = "SELECT *
FROM " . USER_TABLE . "
WHERE
" . USER_TABLE_LOGIN . " =
'" . $session->get(USER_LOGIN_VAR) . "'
AND
" . USER_TABLE_PASSW . " =
'" . md5($oldPassword) . "'";
$result = $this->db->query($sql);
if ($result->size() != 1) {
return FALSE;
}
// Update the password
$sql = "UPDATE " . USER_TABLE . "
SET
" . USER_TABLE_PASSW . " =
'" . md5($newPassword) . "'
WHERE
" . USER_TABLE_LOGIN . " =
'" . $session->get(USER_LOGIN_VAR) . "'";
$result = $this->db->query($sql);
if (!$result->isError()) {
// Store the new credentials
$auth->storeAuth($session->get(USER_LOGIN_VAR),
$newPassword);
return TRUE;
} else {
return FALSE;
}
}
The changePassword method accepts three parameters: an instance of the Auth class (so it can use the storeAuth method it provides), an old password, and a new password.
It first checks that the combination of the old password and the login name (which it retrieves from the session) are correct. It's a good idea to require the old password before changing it to something else; perhaps the user logged in at an Internet café and then left, forgetting to log out, or worse—their session was hijacked. This process at least precludes some potential damage, as it prevents anyone who "takes over" the session being able to change the password and thus assume total control. Instead, they're only logged in as long as the session continues.
It's time to put this into action in the page to which the login form submits…
Example 1.43. 12.php (excerpt)
<?php
// Include MySQL class
require_once 'Database/MySQL.php';
// Include Session class
require_once 'Session/Session.php';
// Include Authentication class
require_once 'AccessControl/Auth.php';
// Include AccountMaintenance class
require_once 'AccessControl/AccountMaintenance.php';
// Include QuickForm class
require_once 'HTML/QuickForm.php';
$host = 'localhost'; // Hostname of MySQL server
$dbUser = 'harryf'; // Username for MySQL
$dbPass = 'secret'; // Password for user
$dbName = 'sitepoint'; // Database name
// Instantiate MySQL connection
$db = &new MySQL($host, $dbUser, $dbPass, $dbName);
// Instantiate the Authentication class
$auth = &new Auth($db, '11.php', 'secret');
We include all the classes needed for the code, then instantiate the MySQL and Auth classes. From the moment Auth is instantiated, the provision of an invalid login/password combination will see the visitor returned to the login form.
This time, just for variety, we use a switch statement to control the flow of logic on the page:
Example 1.44. 12.php (excerpt)
switch ($_GET['view']) {
case 'changePassword':
// Instantiate the QuickForm class
$form = new HTML_QuickForm('changePass', 'POST',
'12.php?view=changePassword');
// A function for comparing password
function cmpPass($element, $confirm)
{
global $form;
$password = $form->getElementValue('newPassword');
return $password == $confirm;
}
// Register the compare function
$form->registerRule('compare', 'function', 'cmpPass');
// Add a header to the form
$form->addHeader('Change your Password');
// Add a field for the old password
$form->addElement('password', 'oldPassword',
'Current Password');
$form->addRule('oldPassword', 'Enter your current password',
'required', false, 'client');
// Add a field for the new password
$form->addElement('password', 'newPassword', 'New Password');
$form->addRule('password', 'Please provide a password',
'required', FALSE, 'client');
$form->addRule('password',
'Password must be at least 6 characters',
'minlength', 6, 'client');
$form->addRule('password',
'Password cannot be more than 12 chars',
'maxlength', 50, 'client');
$form->addRule('password',
'Password can only contain letters and ' .
'numbers', 'alphanumeric', NULL, 'client');
// Add a field for the new password
$form->addElement('password', 'confirm', 'Confirm Password');
$form->addRule('confirm', 'Confirm your password',
'compare', false, 'client');
// Add a submit button
$form->addElement('submit', 'submit', 'Change Password');
Here, the usual QuickForm setup code builds a form for changing passwords. Notice again the cmpPass function, which compares the new password and the rules for the newPassword field. We perform most of the validation of the new password within the form, leaving it to the AccountMaintenance class to perform a final check against the old password before making the change.
On validation of the form, we instantiate the AccountMaintenance class and tell it to change the password.
Example 1.45. 12.php (excerpt)
// If the form is submitted...
if ($form->validate()) {
// Instantiate Account Maintenance class
$aMaint = new AccountMaintenance($db);
// Change the password
if ($aMaint->changePassword(
$auth,
$form->getSubmitValue('oldPassword'),
$form->getSubmitValue('newPassword'))) {
echo 'Your password has been changed successfully.
'<br />Click <a href="' .$_SERVER['PHP_SELF'] .
'">here</a>';
} else {
echo 'Error changing your password.<br />' .
'Click <a href="' . $_SERVER['PHP_SELF'] .
'">here</a>';
}
} else {
// If not submitted, display the form
$form->display();
}
break;
The script finishes with the default behavior of the switch statement; a simple menu is displayed, providing users with the option to change their passwords.
Example 1.46. 12.php (excerpt)
default:
echo '<b>Options:</b><br />';
echo '<a href="' . $_SERVER['PHP_SELF'] .
'?view=changePassword">Change Password</a>';
break;
}
?>
Now that you know how to change passwords, it should be no problem for you to change other account settings, such as the first and last names and the signature, by adding these to the AccountMaintenance class. If you want to allow users to change their email address, you'll need to examine the registration procedure used earlier in this chapter, and modify the SignUp class. You should make sure that users confirm a new email address before you allow them to change it.