How do I run validation checks on a password field in CakePHP, seeing that the password is hashed before I get a chance to run any checks on it?
If you only have a single password field in your form, you will need to create a custom hash function that either does nothing, or, better, preserves the original password somewhere.
Most likely though you have two password fields in your form where the user is required to confirm the password. In this case, you perform your password validation rules on the second password field. This can automatically happen in a custom validation rule, remember that you have access to all other fields inside a validation function via $this->data. You can then confirm that the two passwords are identical as described here.
It works this way for me (in the model):
public $validate = array(
'password' => array(
'minLength' => array(
'rule' => array('minLength', '8')
)
)
);
If you want to do more validations then create a custom validation method in the appropriate model. In the custom validation method hash password this way: Security::hash($this->data['User']['password'], null, true)
Related
I cannot get the validation to work properly when updating entity data. The validation does not work after changing the initial data. The code below provides an example:
// in controller
$user = $this->Users->newEntity([
'mail' => 'wrong',
'password' => 'password',
'auth_level' => 0,
]);
debug($user->getErrors()); // Will show error: wrong email format
$user->mail = "correct#correct.correct";
debug($user->getErrors()); // Will show no errors
$user->mail = "wrong";
debug($user->getErrors()); //Will show no errors
if (TableRegistry::get('users')->save($user)) {
// This actually executes
}
My validation rule in the model is as follows:
public function validationDefault(Validator $validator): Validator
{
$validator
->email('mail')
->add('mail', 'unique',
[
'on' => ['create', 'update'],
'rule' => 'validateUnique',
'provider' => 'table',
'message' => "Email already in use"
]
);
return $validator
}
I tried creating rules with "on" => "update", but it does not help.
What I am trying to achieve is to get an entity, change the email address and save it back to database. Upon saving, the email field is neither validated for format nor uniqueness.
For the sake of completeness.
There is a difference between Application Rules and Validation Rules.
Validation Rules validate data typically coming from user's input (newEntity(), patchEntity()). Entity is regenerated. Baked ones are in "validationDefault" function within Tables.
Application Rules establish some rules for data modified within application code which is expected to be 'safe' (setters). Entity is not regenerated. Baked ones are in "buildRules" function within Tables.
"save()" function won't go through validation rules, but through application rules.
When saving data that needs to pass through validation rules because it's assigned/set within the application but it's data coming from user's input, make sure you use patchEntity().
More info: https://github.com/cakephp/cakephp/issues/6654
The solution is to always update entity data with
$model->patchEntity($entity, $newdata)
I am using CakePHP for my application, where I have a User model. This User has a password, which has a regex to validate.
The regex forces the user to use a password at least 6 characters long, containing at least 1 number and special char.
The validation looks like this:
'password' => array(
'ruleName' => array(
'rule' => array('custom', '/^.*(?=.{6,})(?=.*\d)(?=.*[a-zA-Z])(?=.*[##$%^&+=]).*$/i'),
'message' => 'Password not legit',
'allowEmpty' => true
)
)
When I want to edit my password, this validation works great. But when I want to edit the user (no option to change password there), the $this->User->save() fails.
If I debug my $this->User->validationErrors, the only thing shown is:
array(
'password' => '*****'
)
The password field is not set in my post data, so the validation should not happen at all.
When I comment this block of validation code, the user can be saved.
Anyone knows what I am doing wrong?
I solved it myself already. Before saving the User object, I already did a $this->User->Read(null, $userid) for other purposes.
This resulted in remembering the values of the read (including password) in the $this->User object.
Since the save method is called on the $this->User object, the password value is trying to get saved too. But since *** isn't valid according to the regex, the save fails.
Thanks for the help anyway!
I was investigating CakePHP (2.3.4) Model::save method to insert new records and ran into a little snag:
//Two fields provided, email field intended to fail validation
$data = array('Member' => array(
'username' => 'hello',
'email' => 'world'
));
$this->Member->create();
var_dump($this->Member->save($data, true));
The above save() will return false and no data will be written to the database. However if I change the data to:
//One field provided, intended to pass validation
$data = array('Member' => array(
'username' => 'hello'
));
then save() will attempt to write a new record to database with a blank email field. I realize that skipping validation for unspecified fields might be a useful behavior during updates, but is there a CakePHP recommended way to handle partially empty data sets when creating new records? Thanks a lot.
Edit:
Thanks to Sam Delaney for the tip. In case anybody else gets stumped, this did the trick: CakePHP Data Validation: field 'required' key
This key accepts either a boolean, or create or update. Setting this key to true will make the field always required. While setting it to create or update will make the field required only for update or create operations. If ‘required’ is evaluated to true, the field must be present in the data array. For example, if the validation rule has been defined as follows:
I had originally baked the model and forgotten that required => true was left out of the validation rule. Setting it to true or 'create' would've avoided me blank records getting inserted due to gibberish data array.
IMHO what you've experienced is the desired behavior. Consider that you have the same two fields within a form and the user provides a value for only username. Both username and email field are submitted even though email is empty. Then on the server, you try to save the record only to find out that it has failed validation which you feedback to the user. On the other hand, perhaps in your system it is perfectly possible to create a user without requiring an email (for example root access account), CakePHP's validation implementation allows both of these scenarios.
If this flexibility isn't what you desire, just set the required attribute for your validation rule as true:
public $validate = array(
'email' => array(
'rule' => 'email',
'required' => true
)
);
This will satisfy your all or nothing requirement.
I dont want to update the password fields.how to use this.Im using md5 encode for password.So i dont want to update the password field in yii framework.any help appreciated??
I think a better approach would be to not use the scenario in this case. The next code in the rules just says to the scenario: the next fields are required. But not: skip the other else.
array('name, username, email', 'required', 'on' => 'update'),
For example, if we limit the length of the password up to 32 characters, but in a database is stored in a format sha1 (length 40), then we have a problem because the validator will block the database query.This is because when you make updating, the "validatе" method checks all class properties (regards database table mapping), not just the new ones delivered by post.
Could use the method "saveAttributes", but then I noticed another problem. If the column "email" is unique in the database and in case edited email duplicate one of the existing, then the Yii message system defined in the rules can not notify and throws error code regards database query.
The easiest approach I think is: don't set scenario in this case. Just send as an argument the properties you want. This will keep the all CRUD features created by GII.
In your code it looks like this:
(in model)
public function rules() {
return array(
array('name, username, email, password', 'required'),
);
}
(in controller)
if($id==Yii::app()->user->id){
$model=$this->loadModel($id);
if(isset($_POST['JbJsJobResume'])) {
$model->attributes=$_POST['JbJsJobResume'];
if($model->save(true, array('name', 'username', 'email')))
$this->redirect(array('view','id'=>$model->id));
}
$this->render('update',array( 'model'=>$model, ));
}
I noticed that you do not use RBAC. It is very convenient and flexible - try it.
http://www.yiiframework.com/doc/guide/1.1/en/topics.auth#role-based-access-control
In your model you must do something like this:
public function rules() {
return array(
array('name, username, email, password', 'required', 'on' => 'create'),
array('name, username, email', 'required', 'on' => 'update'),
);
}
Lets say that the scenario that you run now is the update. So I don't require the password there. I require it only in the create scenario that you may have. So in the view file that you have you remove the password field and inside the action that you have you include this:
$model->setScenario('update');
so it will not require the password and it will remain the same.
For the password change you can create a new action (ex. actionPassChange) where you will require to type twice the new password.
$model->attributes=$_POST['JbJsJobResume'];
instead of assign all attributes just assign those only you want to save,
as
$model->name=$_POST['JbJsJobResume']['name'];
$model->save();
1st option Just unset password field before setting it:
function update(){
$model=$this->loadModel($id);
unset($_POST['JbJsJobResume']['password']);
$model->attributes=$_POST['JbJsJobResume'];
$model->save();
}
2nd option: Use temp variable:
function update(){
$model=$this->loadModel($id);
$temPassword = $model->passwrod;
$model->attributes=$_POST['JbJsJobResume'];
$model->passwrod = $temPassword;
$model->save();
}
3rd option: use scenarios
I am not sure why this is a problem, and some code could help us to understand why. If you do not wish to capture / update the password, then why is the password field in the form?
If you remove the password field from the view, the value of the password field will not be posted back to controller and then it will not be updated.
There is a possibility that the above method does not work and this could be that in your User model, you are encrypting the password in the afterValidate method?:
protected function afterValidate()
{
parent::afterValidate();
$this->password = $this->encrypt($this->password);
}
public function encrypt($value)
{
return md5($value);
}
In this scenario, if you remove the password field from the view, and just update the name, username or email, then the md5 hash of the password will be re-hashed automatically and you will lose the real password.
One method to get around this is to md5 the password in the afterValidate method (create or update) however if the user wishes to change profile details, in the same form, ask the user to verify their password again.
FORM: user changes name and verifies password
Form posted
Controller calls authenticate method.
If authenticate true, overwrite the entry in user table (including verified pw)
I think #Gravy's answer is right,Thanks Gravy and Nikos Tsirakis. I have fixed nearly same issue as #faizphp. I add scenario for User model as Nikos Tsirakis said, but got same issue also. Then I found I encrypt password in User.afterValidate, so when update the User model everytime, the program encrypt the password in database again to wrong password. So i changed my function from
protected function afterValidate()
{
parent::afterValidate();
if (!$this->hasErrors())
$this->password = $this->hashPassword($this->password);
}
</code>
to
protected function afterValidate()
{
parent::afterValidate();
if (!$this->hasErrors() && $this->scenario==="create")
$this->password = $this->hashPassword($this->password);
}
.
It seems work.
Want to show a custom message in form's error list, if the two fields did not match.
the from is as follows,
'old_password' =>'Old Password*',
'new_password' =>'New Password*',
'confirm_password' =>'Confirm Password*',
I want that the old password should match the value from the database, the value in new password and confirm password should also match.
please help me.
In Symfony 1.1 and later, to compare if the two form fields match you need to set up a post validator, like:
$this->validatorSchema->setPostValidator(
new sfValidatorSchemaCompare(
'new_password',
sfValidatorSchemaCompare::EQUAL,
'confirm_password',
array(),
array('invalid' => 'Your custom error message here!!')
)
);
Try reading Symfony forms in Action, it should solve most of your problems about form creation and validation within the Symfony framework