Checking old password when changing it

Hii there,

I’m currently making a modelless form so that users can change their password.
I already got it to change the password itself, but it also has to check the currentPassword field against the password field in the database.

this is my current validation code in Form/ChangePasswordForm.php:

protected function _buildValidator(Validator $validator) {
  $validator
  /* some other rules here that are not relevant */
  // Rules for the currentPassword field
  ->requirePresence('currentPassword')
  ->notEmpty('currentPassword', 'Please enter your current password')
  // Check whether the currentPassword is valid
  ->add('currentPassword','custom',[
    'rule' => function($value,$context){
      $query = $this->find()->where(['id'=>$context['data']['id']])->first();
      $data = $query->toArray();
      return(new DefaultPasswordHasher)->check($value,$data['password']);
    },
    'message' => 'Current password is invalid'
  ]);
  return $validator;
}

this, however, gives Call to undefined method App\Form\ChangePasswordForm::find().
Is there a way to fix this issue?
I know I could put the validator in the model, but I want to keep the form modelless because it only changes the password.

Whether it changes only 1 field or a dozen it doesn’t matter. Use a “regular” form by passing user entity to Form::create().

I’m currently making a modelless form

Your form is intended to manipulate data which belongs to the model ($user->password). Therefore it interacts with the model-layer. If so it should not/can not be “modelless”.

Like ADmad said: Use a regular form. You will access the model-layer anyway.

This also explains your error. It probably comes from this line: $this->find()->where([‘id’=>$context[‘data’][‘id’]])->first(); Here you’re trying to access the model-layer.

Like the documentation states:

[…] but there are times when you’ll need to validate user input and then perform an action if the data is valid. The most common example of this is a contact form.

That’s not your case.

Ah ok, I thought “modelless” in the documentation meant: “is not build using a model”.
So basically all I have to do is pass the user entity to Form::create() instead of the ChangePasswordForm class?

I’ve managed to “solve” it (though in a less cake way, but it works for now) by adding the following code to ChangePasswordForm.php:

public function checkPassword($inputPassword,$user){
      return (new DefaultPasswordHasher)->check($inputPassword,$user->password);
}

then adding calling it by using this in the controller:

$user = $this->Users->find()->where(['id'=> $this->Auth->user('id')])->first();
if($changePasswordForm->checkPassword($this->request->getData('currentPassword'),$user)){
  // is valid
}

I’ll probably get some hate for doing it like this, but until I find a better way to do this conforming the CakePHP conventions, it’ll have to do.

2 Likes

Awesome, it works for me.