Восстановление пароля

Реализация двухшагового восстановления пароля с подтверждением операции по электронной почте. Защита от спама и возможной перегрузки сервера.

  • 00:00:53 Юзкейс сброса пароля
  • 00:02:07 Команда запроса восстановления
  • 00:03:16 Проверка email через Assert
  • 00:04:33 Проектирование метода в сущности
  • 00:07:02 Юнит-тест для запроса на сброс
  • 00:11:08 Реализация метода requestPasswordReset
  • 00:12:01 Тест истечения токена
  • 00:13:01 Проверка работы
  • 00:13:50 Куда поместить логику
  • 00:15:16 Принцип Information Expert из GRASP
  • 00:16:54 Юзкейс ввода нового пароля
  • 00:18:53 Оптимизация производительности
  • 00:21:28 Тесты записи нового пароля
  • 00:23:16 Написание метода resetPassword
  • 00:24:51 Обзор результата
Скрытый контент (код, слайды, ...) для подписчиков. Открыть →
Дмитрий Елисеев
elisdn.ru
Комментарии (13)
Arunas

Спасибо. Об ролях пользователей: будет про групах пользователей, их permision?

Ответить
Дмитрий Елисеев

Будет про привязку разрешений к ролям.

Ответить
Arunas

Спс

Ответить
Bondarenko Alexandr

Кстати, службу для хэширования пароля еще хорошо бы передавать в метод resetPassword, чтобы не дублировать проверку предусловий по хэндлерам: к примеру, бывают предусловия, когда пароль пользователя ограничен каким-то количеством символов, а также должны присутствовать цифры и латиница. Но код станет немного сложнее, т.к. у User'a появится зависимость. В unit тестах придется ее мокать, и в UserBuilder инжектить PasswordHasher.

Ответить
Дмитрий Елисеев

Да. Ещё хэшер удобно передавать в метод changePassword для проверки текущего пароля:

class User
{
    ...

    public function changePassword(string $current, string $new, PasswordHasher $hasher): void
    {
        if (!$hasher->validate($current, $this->passwordHash)) {
            throw new DomainException('Invalid current password.');
        }

        $this->passwordHash = $hasher->hash($new);
    }
}

А если есть условия, то можно при желании обернуть в объект-значение Password.

Ответить
Bondarenko Alexandr

Дмитрий, спасибо за материал! Есть такой вопрос: как вы убеждаетесь, что код для прикладных хэндлеров реализован верно? Тесты для них в проекте отсутствуют.

Ответить
Дмитрий Елисеев

Их будем тестировать сквозными функциональными тестами контроллеров.

Ответить
Олег

Дмитрий, возник такой вопрос.
Почему для генерации токенов в тестах мы не используем токенайзер?
Просто уже второй тест, где мы делаем приватный метод возвращающий токен, при том, что есть целый объект, который эти токены штампует.

Ответить
Дмитрий Елисеев

Чтобы минимизировать использование в тестах других классов. Так как Tokenizer со временем можем дорабатывать и придётся тоже исправлять все тесты.

А чтобы не копипастить приватный метод, можно вынести его в статический метод отдельного класса и вызывать вроде $token = Test\Tokenizer::generate($expires).

Ответить
Олег

Дмитрий, возник вот так вопрос (возможно он решается дальше по курсу). Модуль Auth является, как я понимаю, отдельной доменной зоной, со своими сущностями и так далее. Контроллер ничего не трогает, а лишь обращается к командам. Но тут у нас появляется еще одна доменная зона, аукцион (или еще что). И в командах аукциона нам надо знать о пользователе. Пользователь у нас в доменной зоне Auth. Как мы будет к нему обращаться и работать с ним? Или мы будем использовать Entity из соседнего модуля? Не очень ясно. Плюс если так, то появляется зависимость одного модуля от другого. Какие тут решения? Заранее спасибо за ответ

Ответить
Дмитрий Елисеев

В модуле аукциона мы создадим отдельную сущность участника с тем же id и там будем работать уже с ней.

Ответить
Андрей

Дмитрий возник вопрос.
А почему вы в юнит тестах используете статические методы phpunit, а не через $this?

Ответить
Дмитрий Елисеев

Потому что эти методы в самом PHPUnit статические.

Ответить
Зарегистрируйтесь или войдите чтобы оставить комментарий

Или войти через:

Yandex
MailRu
GitHub
Google