Hello and thank you for such a great tutorial. I followed your steps and made some improvements (I think so). Insteed of md5 I am using Sha512 encrypting with salt key that is changing after every login, but now I got error and can’t find out what is wrong with my code. If you help me I think that my improvement (that doesn’t work without your help) will improve security. Here is what I have now.
It shows this error massege:
Property "Users.password2" is not defined.
/framework/db/ar/CActiveRecord.php(144)
SiteController.php
public function actionRegister()
{
$model = new Users;
// collect user input data
if(isset($_POST['ajax']) && $_POST['ajax']==='register-form')
{
echo CActiveForm::validate($model);
Yii::app()->end();
}
if(isset($_POST['RegsiterForm']))
{
$model->attributes=$_POST['RegsiterForm']; // set all attributes with post values
// NOTE Changes to any $model->value have to be performed BEFORE $model-validate()
// or else it won't save to the database.
// Check if question has a ? at the end
// validate user input and redirect to previous page if valid
if($model->validate())
{
// save user registration
$model->save();
$this->redirect($this->render('finished',array('model'=>$model))); // Yii::app()->user->returnUrl
}
}
// display the registration model
$this->render('register',array('model'=>$model));
}
Register.php (The view file, form was generated from Gii)
<div class="form">
<?php $form=$this->beginWidget('CActiveForm', array(
'id'=>'register-form',
'enableAjaxValidation'=>true,
)); ?>
<p class="note">Fields with <span class="required">*</span> are required.</p>
<?php echo $form->errorSummary($model); ?>
<div class="row">
<?php echo $form->labelEx($model,'username'); ?>
<?php echo $form->textField($model,'username'); ?>
<?php echo $form->error($model,'username'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'password'); ?>
<?php echo $form->textField($model,'password'); ?>
<?php echo $form->error($model,'password'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'password2'); ?>
<?php echo $form->textField($model,'password2'); ?>
<?php echo $form->error($model,'password2'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'email'); ?>
<?php echo $form->textField($model,'email'); ?>
<?php echo $form->error($model,'email'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'fullname'); ?>
<?php echo $form->textField($model,'fullname'); ?>
<?php echo $form->error($model,'fullname'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'question'); ?>
<?php echo $form->textField($model,'question'); ?>
<?php echo $form->error($model,'question'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'answer'); ?>
<?php echo $form->textField($model,'answer'); ?>
<?php echo $form->error($model,'answer'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'verifyCode'); ?>
<?php echo $form->textField($model,'verifyCode'); ?>
<?php echo $form->error($model,'verifyCode'); ?>
</div>
<div class="row buttons">
<?php echo CHtml::submitButton('Submit'); ?>
</div>
<?php $this->endWidget(); ?>
</div><!-- form -->
user.php (the model)
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('username','length','max'=>16),
// convert username to lower case
array('username', 'filter', 'filter'=>'strtolower'),
array('password','length','max'=>32, 'min'=>6),
array('password2','length','max'=>32, 'min'=>6),
// compare password to repeated password
array('password', 'compare', 'compareAttribute'=>'password2'),
array('email, fullname', 'length', 'max'=>128),
// make sure email is a valid email
array('email','email'),
// make sure username and email are unique
array('username, email, password, salt', 'unique'),
// convert question to lower case
array('question', 'filter', 'filter'=>'strtolower'),
array('answer','length','max'=>64),
// convert answer to lower case
array('answer', 'filter', 'filter'=>'strtolower'),
array('privilages, worns, status', 'numerical', 'integerOnly'=>true),
array('privilages','length','max'=>4, 'min'=>1),
array('worns','length','max'=>3, 'min'=>0),
array('status','length','max'=>2, 'min'=>0),
array('username, password, password2, salt, email, fullname, register_date, login_date, privilages, worns, status', 'required'),
// verifyCode needs to be entered correctly
array('verifyCode', 'captcha', 'allowEmpty'=>!extension_loaded('gd')),
);
}
[..........]
public function beforeSave()
{
while ($record2 === null){
$salt = Randomness::randomString(32);
$record2 = Users::model()->findByAttributes(array('salt'=>$salt));
}
$pass = hash('sha512', $this->password.$salt);
$this->salt = $salt;
$this->password = $pass;
return true;
}
and UserIdentity.php
<?php
/**
* UserIdentity represents the data needed to identity a user.
* It contains the authentication method that checks if the provided
* data can identity the user.
*/
class UserIdentity extends CUserIdentity
{
private $_id;
public function authenticate()
{
$record=Users::model()->findByAttributes(array('username'=>$this->username));
if($record===null)
$this->errorCode=self::ERROR_USERNAME_INVALID;
else if($record->password!==hash('sha512', $this->password.$record->salt))
$this->errorCode=self::ERROR_PASSWORD_INVALID;
else
{
while ($record2 !== null){
$salt = Randomness::randomString(32);
$record2 = Users::model()->findByAttributes(array('salt'=>$salt));
}
$record->salt = $salt;
$record->password = hash('sha512', $this->password.$salt);
$record->save;
$this->_id=$record->id;
$this->setState('user_id', $record->id);
$this->setState('user_username', $record->username);
$this->setState('user_privilages', $record->privilages);
$this->errorCode=self::ERROR_NONE;
}
return !$this->errorCode;
}
public function getId()
{
return $this->_id;
}
}
Also I must mention that I am using the Randomness extention .