Forcing a Password Change
I looked around at a few methods for forcing users to change their password. Typically you may allocate a user an initial generated password however users will often prefer to have a password they are able to remember. This post will run through allocating a user a generated password and then forcing the user to change the password at their first login. I could not find an easy to follow method all in one place explaining the methodology so I thought I would try to bring the whole process together in one place.First we will create an initial password for the new user and there are a number of ways to achieve this however I will show you a simple way to create an initial password. In your user controller when creating a new user the following function is simple and easy to implement. Let us assume we are creating a new user that needs a password automatically generated. Create a function in your controller.
public function createPassword()
{
$chars = "abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789";
$str="";
$size = strlen( $chars );
for( $i = 0; $i < 9; $i++ ) {
$str .= $chars[ rand( 0, $size - 1 ) ];
}
return $str;
}
$client_password = $this->createPassword();
$new_client = new Client;
$new_client->real_name = 'Joe';
$new_client->username = 'cleric';
$new_client->password = sha1($client_password);
$new_client->active = 1;
$new_client->save();
When I create a web application I usually write it in such a way that a user must be logged in to access any page within the application. I will not run through how to do this however look at the article written by Larry Ullman - Forcing Login for All Pages in Yii. The benefit to using Ullman's approach is that just a small amount of code will add authorisation to the whole web site and you can re-use the method in other applications as the implementation is very straight forward.
Now in the SiteController in the function actionLogin test to see if the last_login field is null. If that is the case then redirect the user to allow the user to change their password to something easier to remember. Do a simple query to check the last_login for the user in the actionLogin method in your SiteController. For example:
public funcion actionLogin()
{
.........
//user login for first time
$upduser=User::model()->findByPk($userId);
if ($upduser->last_login == '') {
$this->redirect('index.php?r=user/firstlogin');
}
else // client has logged in previously
{
$upduser=User::model()->findByPk($id]);
$upduser->last_login = new CDbExpression('NOW()'); //update the users last_login
$upduser->save();
$this->redirect('index.php?r=user/welcome');
}
}
If the last_login field is null the user will get redirected to the UserController::firstlogin method. The methodology I use is adapted from a good StackOverflow discussion which you can find at Yii, best way to implement “user change of password”.{
.........
//user login for first time
$upduser=User::model()->findByPk($userId);
if ($upduser->last_login == '') {
$this->redirect('index.php?r=user/firstlogin');
}
else // client has logged in previously
{
$upduser=User::model()->findByPk($id]);
$upduser->last_login = new CDbExpression('NOW()'); //update the users last_login
$upduser->save();
$this->redirect('index.php?r=user/welcome');
}
}
In your UserController create a function called actionFirstLogin: An example:
//function for clients to allow them to change their password on the first login
public function actionFirstlogin()
{
$model=new ChangePasswordForm();
if (isset($_POST['ChangePasswordForm'])) {
$model->setAttributes($_POST['ChangePasswordForm']);
if ($model->validate()) {
$model->saveNewPassword();
$this->redirect('index.php?r=client/postaldata'); //or wherever you want to redirect the user
}
}
$this->render('updatefirst', array('model'=>$model));
}
?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'=>'newPasswordRepeat'),
array('newPassword', 'match', 'pattern'=>'/^[a-z0-9_\-]{8,}/i', 'message'=>'Your password be 8 characters (letters/numbers) in length.'),
);
}
/**
*
* @return string Hashed password
*/
protected function createPasswordHash($password)
{
return sha1($password);
}
/**
*
* @return string
*/
protected function getUserPassword()
{
$curr_user = Yii::app()->db->createCommand()
->select('id, password')
->from('user')
->where('username=:uid', array(':uid'=>Yii::app()->user->uname)) //using a session variable
->queryRow();
return $curr_user['password'];
}
/**
* Saves the new password.
*/
public function saveNewPassword()
{
$user=User::model()->findByAttributes(array('username'=>Yii::app()->user->uname));
$user->password = $this->createPasswordHash($this->newPassword);
$user->last_login = new CDbExpression('NOW()');
$user->update();
}
/**
* Validates current password.
*
* @return bool Is password valid
*/
public function validateCurrentPassword()
{
return $this->createPasswordHash($this->currentPassword) == $this->getUserPassword();
}
}
Remember I am just using a simple SHA hash to store my passwords in this example.
You will now need to create a view that will take 3 inputs - Current Password, New Password, Repeat New Password. This view in this example is called updatefirst.php and the view is in the User view directory. The code for this view to allow the input of the old and new password will need to be modified to your needs. An example of the updatefirst.php view file code:
NOTE: Again there are some session variables in this code Yii::app()->user->title) and I am fond of the bootstrap extension hence the bootstrap widgets. Finally I am using renderPartial so you will need 2 view files
<h4>Enter new password for <?php echo Yii::app()->user->title; ?></h4>
<?php $this->widget('bootstrap.widgets.TbLabel', array(
'type'=>'info', // 'success', 'warning', 'important', 'info' or 'inverse'
'label'=>'You must change your password - Should be 8 characters long (mixture of letters and numbers',
));
echo '<br /><br />';
$this->renderPartial('_updateformfirst', array('model'=>$model)); ?>
=========================================================
Now the code for the _updateformfirst.php file
<?php
/* @var $this UserController */
/* @var $model User */
/* @var $form CActiveForm */
?>
<div class="wide form">
<?php $form=$this->beginWidget('CActiveForm', array(
'id'=>'user-form',
'enableAjaxValidation'=>false,
)); ?>
<p class="note">Fields with <span class="required">*</span> are required.</p>
<?php echo $form->errorSummary($model); ?>
<div class="row">
<?php echo "Enter your current password <span style = 'color:red'><sup>*</sup></span>"; ?>
<?php echo $form->passwordField($model,'currentPassword',array('class'=>'span2','maxlength'=>20)); ?>
</div>
<div class="row">
<?php echo "Enter your new password <span style = 'color:red'><sup>*</sup></span>".' '; ?>
<?php echo $form->passwordField($model,'newPassword',array('class'=>'span2','maxlength'=>20)); ?>
</div>
<div class="row">
<?php echo "Repeat your new password <span style = 'color:red'><sup>*</sup></span>".' '; ?>
<?php echo $form->passwordField($model,'newPasswordRepeat',array('class'=>'span2','maxlength'=>20)); ?>
</div>
<div class="row buttons">
<?php echo CHtml::submitButton('Update Password'); ?>
</div>
<?php $this->endWidget(); ?>
</div><!-- form -->
While there are a few steps to creating this workflow for a password change it is fairly easy to implement if you follow the steps outlined. For more information about changing passwords (such as forcing password changes on a time basis e.g. every month) take a look at :
- Model password confirmation field
- Force a User to Change Their Password (ChangePasswordFilter)
- Change Password Action Required - Ideas?
- Various Yii Forum Search Results
No comments:
Post a Comment