Yii, best way to implement "user change of password"

mahsa.teimourikia picture mahsa.teimourikia · Nov 5, 2012 · Viewed 12.5k times · Source

I'm using Yii for an application, I'm writing a very simple user management, like registering, deleting and updating users... For updating the existing user I need to check the old password first before change it to the new inserted password. So here is the fields I have in the form:

username:----
old_password:---
new_password:---

and my user table looks like this:

id, username, password

How can I validate the old_password before updating it with the new_password? I know the usual php coding, but I want to know if there are any Yii tricks that does this automatically...

Thanks in advance

Answer

emix picture emix · Feb 17, 2013

You should not pollute your model with rubbish. Please, always have in mind these basic MVC principles:

  • Your controller must not be aware of your model's implementation.
  • Don't pollute your model with stuff not connected with your application's business model.

Always create reusable code, make your code "DRY" (Don't repeat yourself)

By the way, what is the purpose of the username field? Since the form would be available to the logged user only, the username can be accessed already with Yii::app()->user.

<?php
// models/ChangePasswordForm.php

class ChangePasswordForm extends CFormModel
{
    /**
     * @var string
     */
    public $currentPassword;

    /**
     * @var string
     */
    public $newPassword;

    /**
     * @var string
     */
    public $newPasswordRepeat;

    /**
     * Validation rules for this form.
     *
     * @return array
     */
    public function rules()
    {
        return array(
            array('currentPassword, newPassword, newPasswordRepeat', 'required'),
            array('currentPassword', 'validateCurrentPassword', 'message'=>'This is not your password.'),
            array('newPassword', 'compare', 'compareAttribute'=>'validateNewPassword'),
            array('newPassword', 'match', 'pattern'=>'/^[a-z0-9_\-]{5,}/i', 'message'=>'Your password does not meet our password complexity policy.'),
        );
    }

    /**
     * I don't know your hashing policy, so I assume it's simple MD5 hashing method.
     * 
     * @return string Hashed password
     */
    protected function createPasswordHash($password)
    {
        return md5($password);
    }

    /**
     * I don't know how you access user's password as well.
     *
     * @return string
     */
    protected function getUserPassword()
    {
        return Yii::app()->user->password;
    }

    /**
     * Saves the new password.
     */
    public function saveNewPassword()
    {
        $user = UserModel::findByPk(Yii::app()->user->username);
        $user->password = $this->createPasswordHash($this->newPassword);
        $user->update();
    }

    /**
     * Validates current password.
     *
     * @return bool Is password valid
     */
    public function validateCurrentPassword()
    {
        return $this->createPasswordHash($this->currentPassword) == $this->getUserPassword();
    }
}

example controller action:

public function actionChangePassword()
{
    $model=new ChangePasswordForm();
    if (isset($_POST['ChangePasswordForm'])) {
        $model->setAttributes($_POST['ChangePasswordForm']);
        if ($model->validate()) {
            $model->save();
            // you can redirect here
        }
    }

    $this->render('changePasswordTemplate', array('model'=>$model));
}

example template code:

    <?php echo CHtml::errorSummary($model); ?>

    <div class="row">
        <?php echo CHtml::activeLabel($model,'currentPassword'); ?>
        <?php echo CHtml::activePasswordField($model,'currentPassword') ?>
    </div>

    <div class="row">
        <?php echo CHtml::activeLabel($model,'newPassword'); ?>
        <?php echo CHtml::activePasswordField($model,'newPassword') ?>
    </div>

    <div class="row">
        <?php echo CHtml::activeLabel($model,'newPasswordRepeat'); ?>
        <?php echo CHtml::activePasswordField($model,'newPasswordRepeat') ?>
    </div>

    <div class="row submit">
        <?php echo CHtml::submitButton('Change password'); ?>
    </div>

    <?php echo CHtml::endForm(); ?>
</div><!-- form -->

The template should be easy enough to create. This code, with some minor tweaks, is ready to be copied & pasted to another Yii project.