Passed
Pull Request — master (#67)
by Daniel
05:36
created

PasswordManager::requestResetEmail()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 4
nop 1
dl 0
loc 19
ccs 0
cts 13
cp 0
crap 20
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the Silverback API Components Bundle Project
5
 *
6
 * (c) Daniel West <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Silverback\ApiComponentsBundle\Helper\User;
15
16
use DateTime;
17
use Doctrine\ORM\EntityManagerInterface;
18
use Silverback\ApiComponentsBundle\Entity\User\AbstractUser;
19
use Silverback\ApiComponentsBundle\Exception\InvalidArgumentException;
20
use Silverback\ApiComponentsBundle\Exception\UnexpectedValueException;
21
use Silverback\ApiComponentsBundle\Repository\User\UserRepository;
22
use Silverback\ApiComponentsBundle\Security\TokenGenerator;
23
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
24
use Symfony\Component\Security\Core\Exception\AuthenticationException;
25
use Symfony\Component\Validator\Validator\ValidatorInterface;
26
27
/**
28
 * @author Daniel West <[email protected]>
29
 */
30
class PasswordManager
31
{
32
    private UserMailer $userMailer;
33
    private EntityManagerInterface $entityManager;
34
    private ValidatorInterface $validator;
35
    private UserRepository $userRepository;
36
    private int $tokenTtl;
37
38
    public function __construct(UserMailer $userMailer, EntityManagerInterface $entityManager, ValidatorInterface $validator, UserRepository $userRepository, int $tokenTtl = 8600)
39
    {
40
        $this->userMailer = $userMailer;
41
        $this->entityManager = $entityManager;
42
        $this->validator = $validator;
43
        $this->userRepository = $userRepository;
44
        $this->tokenTtl = $tokenTtl;
45
    }
46
47
    public function requestResetEmail(string $usernameQuery): void
48
    {
49
        $user = $this->userRepository->findOneBy(['username' => $usernameQuery]);
50
        if (!$user) {
51
            throw new UnexpectedValueException('Username not found');
52
        }
53
54
        if ($user->isPasswordRequestLimitReached($this->tokenTtl)) {
55
            return;
56
        }
57
58
        $username = $user->getUsername();
59
        if (!$username) {
60
            throw new InvalidArgumentException(sprintf('The entity %s should have a username set to send a password reset email.', AbstractUser::class));
61
        }
62
        $user->setNewPasswordConfirmationToken(TokenGenerator::generateToken());
63
        $user->setPasswordRequestedAt(new DateTime());
64
        $this->userMailer->sendPasswordResetEmail($user);
65
        $this->entityManager->flush();
66
    }
67
68
    public function passwordReset(string $username, string $token, string $newPassword): void
69
    {
70
        $user = $this->userRepository->findOneByPasswordResetToken($username, $token);
71
        if (!$user) {
72
            throw new NotFoundHttpException();
73
        }
74
75
        $user->setPlainPassword($newPassword);
76
        $user->setNewPasswordConfirmationToken(null);
77
        $user->setPasswordRequestedAt(null);
78
        $errors = $this->validator->validate($user, null, ['password_reset']);
79
        if (\count($errors)) {
80
            throw new AuthenticationException('The password entered is not valid');
81
        }
82
        $this->persistPlainPassword($user);
83
    }
84
85
    public function persistPlainPassword(AbstractUser $user): AbstractUser
86
    {
87
        $this->entityManager->persist($user);
88
        $this->entityManager->flush();
89
        $user->eraseCredentials();
90
91
        return $user;
92
    }
93
}
94