mistaq
mistaq

Reputation: 385

CakePHP 3 Password Reset

I have the following code to reset my password via a link through email (that works fine).

In my User Controller:

 public function resetpw($token = null) {
        $resetpw = $this->Users->findByToken('id');
        if ($this->request->is('post')) {
            $pw = $this->request->data['password'];
            $pwtable = TableRegistry::get('Users');
            $newpw = $pwtable->find($resetpw);
            $newpw->password = $pw;
            if ($pwtable->save($newpw)) {
                 $this->Flash->success(__('Your password has been successfully updated.'));
                return $this->redirect(['action' => 'login']);
        }
            else {
                $this->Flash->error(__('Your password could not be saved. Please, try again.'));
             }
        }
    }

The Reset Password CTP file:

Resetting your password?

<?= $this->Flash->render(); ?>
<?= $this->Form->create() ?>
    <fieldset>
        <legend><?= __('Please enter your new password.') ?></legend>
        <?= $this->Form->input('password', ['label' => 'New Password']) ?>
        <?= $this->Form->input('confirmpassword', ['type' => 'password', 'label' => 'Confirm New Password']) ?>
    </fieldset>
    <?= $this->Form->button(__('Update password')); ?>
<?= $this->Form->end() ?>

I also have the following rules regarding comparing the two passwords:

public function validatePasswords($validator)
{
    $validator->add('confirmpassword', 'no-misspelling', [
        'rule' => ['compareWith', 'password'],
        'message' => 'The passwords are not the same.',
    ]);
    return $validator;
}

After typing in two identical passwords for both the password & comfirmpassword fields, I get the following error:

Unknown finder method "SELECT Users.id AS Users__id, Users.username AS Users__username, Users.password AS Users__password, Users.email AS Users__email, Users.role AS Users__role, Users.token AS Users__token FROM users Users WHERE Users.token = :c0"

Upvotes: 0

Views: 5470

Answers (1)

AD7six
AD7six

Reputation: 66299

Preamble

There are many odd things in the code in the question so before continuing, some required reading:

These sections aught to give enough information to have a code structure that logically can work. This is mentioned because of the following code in the question:

  • TableRegistry::get('ControllerName') usage - Not necessary
  • Table->findByToken('literal-string') - will always return the same thing (nothing)
  • $table->find($notAString); - does not match the api of the find method

Updating a field is just an edit

It's important to recognize that fundamentally, resetting a user's password is just an edit action. Here is an example of an edit action taken from the blog tutorial:

// src/Controller/ArticlesController.php

public function edit($id = null)
{
    $article = $this->Articles->get($id);
    if ($this->request->is(['post', 'put'])) {
        $this->Articles->patchEntity($article, $this->request->data);
        if ($this->Articles->save($article)) {
            $this->Flash->success(__('Your article has been updated.'));
            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error(__('Unable to update your article.'));
    }

    $this->set('article', $article);
}

There are the following steps:

  • Find an article entity
  • Update the article entity
  • Save the article entity
  • Some error handling

These steps are the same as are required in the question scenario. Adapting the above code to the question this code becomes:

// src/Controller/UsersController.php

public function edit($token = null)
{
    $user = $this->Users->findByToken($token)->first();
    if (!$user) {
        $this->Flash->success(__('No user with that token found.'));
        return $this->redirect('/');
    }

    if ($this->request->is(['post'])) {
        $user->password = $this->request->data['password'];
        $user->confirmpassword = $this->request->data['confirmpassword'];
        if ($this->Users->save($user)) {
            $this->Flash->success(__('Your password has been successfully updated.'));
            return $this->redirect(['action' => 'login']);
        }
        $this->Flash->error(__('Your password could not be saved. Please, try again.'));
    }
}

Hashing the password?

It's not mentioned in the question but the above code expects the user entity to hash the password when it's set:

You are responsible for hashing the passwords before they are persisted to the database, the easiest way is to use a setter function in your User entity:

namespace App\Model\Entity;

use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Entity;

class User extends Entity
{

    // ...

    protected function _setPassword($password)
    {
        if (strlen($password) > 0) {
          return (new DefaultPasswordHasher)->hash($password);
        }
    }

    // ...
}

Otherwise, the plain text password will be saved in the users table and not the hash, and login will not work.

Upvotes: 3

Related Questions