Application Security: Changing Passwords

For the applications I build, user passwords are (usually) initially system-generated, which means they appear to be an inscrutable mess of letters and numbers. Most users want to have some control over their own passwords, which means I need to provide a form that lets them change it.

Application Module

Whether I am creating a user editor available to administrators, or an account profile dropbox for users, the general approach is the same. The "editor" module requests a new password (and requires the user to repeat it to make sure the password is what they think it is).

Partial Listing: rsrc/editor/UserEditor.php

class UserEditor extends ApplicationEditor
{
   ...

   function formElements()
   {
      return array(
         'first'    => new IWFormElement('First:', new IWTextElement(10, 16)),
         'last'     => new IWFormElement('Last:', new IWTextElement(15, 24)),
         'email'    => new IWFormElement('Email:', new IWTextElement(25, 64)),
         'gap_one'  => new IWGapElement(10),
         'username' => new IWFormElement('Username:', new IWTextElement(10, 12)),
         'password' => new IWFormElement('New Password:',
                       new IWPasswordElement(10,12)),
         'repeat'   => new IWFormElement('Repeat Password:',
                       new IWPasswordElement(10,12)),
      );
   }
}

Model Object

For the model object class, the password column in the underlying database table typically has a NOT NULL constraint (which is reflected in the default validation for the ORM-generated default model object class). So, I need to override the password validation to consider the circumstances of someone updating their password, which will hopefully be illustrated by the comments below.

The final update, which is also made in the model object class, involves handling the password properly when the user record is saved. Specifically, I want to make sure the new password is md5-encrypted. If the user did not change the password, then I need to remove the password field from consideration for the database update (in the event other fields were changed in the form).

Partial Listing: rsrc/model/application/AppUser.php

class AppUser extends DefaultAppUser
{
   public $repeat;  // the repeat password from the form

   ...

   function validatePassword()
   {
      // For a new user, a password is required
      if ($this->isNew())
      {
         if (! $this->applyRule('IWRequiredRule', 'password'))
         {
            return 'A password is required.';
         }
      }

      // For an existing user, a missing "new password" is okay
      if (! $this->applyRule('IWRequiredRule', 'password')) return false;

      // If a "new password" is present, it must match the value of "repeat"
      if ($this->password != $this->repeat)
      {
         return 'The new password and repeated new password do not match.';
      }
   }

   // Often other processes to be handled here
   function willSave()
   {
      $this->handlePassword();
   }

   function handlePassword()
   {
      if ($this->password and (strlen($this->password) != 32))
      {
         // Use the setter method to ensure the change is saved
         $this->setPassword(md5($this->password));
      }
      else
      {
         // Remove the property so an empty password is not saved
         $this->removeProperty('password');
      }
   }
}

Obviously, the specific password handling can be more complex (and robust), but this general approach is the best practice for organizing password-related code. For example, a more sophisticated password might be required. In that case, the validatePassword() method would need to be updated with additional criteria.