Passed
Push — master ( 52a18c...705018 )
by Daniel
11:52
created

UserDataProcessor::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
nc 1
nop 7
dl 0
loc 16
ccs 0
cts 8
cp 0
crap 2
rs 10
c 1
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 Silverback\ApiComponentsBundle\Entity\User\AbstractUser;
17
use Silverback\ApiComponentsBundle\Exception\InvalidArgumentException;
18
use Silverback\ApiComponentsBundle\Exception\UnexpectedValueException;
19
use Silverback\ApiComponentsBundle\Repository\User\UserRepository;
20
use Silverback\ApiComponentsBundle\Security\TokenGenerator;
21
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
22
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
23
24
/**
25
 * @author Daniel West <[email protected]>
26
 */
27
class UserDataProcessor
28
{
29
    private UserPasswordEncoderInterface $passwordEncoder;
30
    private UserRepository $userRepository;
31
    private EncoderFactoryInterface $encoderFactory;
32
    private bool $initialEmailVerifiedState;
33
    private bool $verifyEmailOnRegister;
34
    private bool $verifyEmailOnChange;
35
    private int $tokenTtl;
36
37
    public function __construct(
38
        UserPasswordEncoderInterface $passwordEncoder,
39
        UserRepository $userRepository,
40
        EncoderFactoryInterface $encoderFactory,
41
        bool $initialEmailVerifiedState,
42
        bool $verifyEmailOnRegister,
43
        bool $verifyEmailOnChange,
44
        int $tokenTtl = 8600
45
    ) {
46
        $this->passwordEncoder = $passwordEncoder;
47
        $this->userRepository = $userRepository;
48
        $this->encoderFactory = $encoderFactory;
49
        $this->initialEmailVerifiedState = $initialEmailVerifiedState;
50
        $this->verifyEmailOnRegister = $verifyEmailOnRegister;
51
        $this->verifyEmailOnChange = $verifyEmailOnChange;
52
        $this->tokenTtl = $tokenTtl;
53
    }
54
55
    public function updatePasswordConfirmationToken(string $usernameQuery): ?AbstractUser
56
    {
57
        $user = $this->userRepository->findOneBy(['username' => $usernameQuery]);
58
        if (!$user) {
59
            throw new InvalidArgumentException('Username not found');
60
        }
61
62
        if ($user->isPasswordRequestLimitReached($this->tokenTtl)) {
63
            return null;
64
        }
65
66
        $username = $user->getUsername();
67
        if (!$username) {
68
            throw new UnexpectedValueException(sprintf('The entity %s should have a username set to send a password reset email.', AbstractUser::class));
69
        }
70
        $user->setNewPasswordConfirmationToken($this->passwordEncoder->encodePassword($user, $token = TokenGenerator::generateToken()));
71
        $user->plainNewPasswordConfirmationToken = $token;
72
        $user->setPasswordRequestedAt(new \DateTime());
73
74
        return $user;
75
    }
76
77
    public function passwordReset(string $username, string $token, string $newPassword): ?AbstractUser
78
    {
79
        $user = $this->userRepository->findOneWithPasswordResetToken($username);
80
        if (!$user) {
81
            return null;
82
        }
83
        $encoder = $this->encoderFactory->getEncoder($user);
84
        if (!$encoder->isPasswordValid($user->getNewPasswordConfirmationToken(), $token, $user->getSalt())) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $user->getSalt() targeting Silverback\ApiComponents...AbstractUser::getSalt() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
It seems like $user->getNewPasswordConfirmationToken() can also be of type null; however, parameter $encoded of Symfony\Component\Securi...face::isPasswordValid() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

84
        if (!$encoder->isPasswordValid(/** @scrutinizer ignore-type */ $user->getNewPasswordConfirmationToken(), $token, $user->getSalt())) {
Loading history...
85
            return null;
86
        }
87
88
        $user->setPlainPassword($newPassword);
89
        $user->setNewPasswordConfirmationToken(null);
90
        $user->setPasswordRequestedAt(null);
91
        $this->encodePassword($user);
92
93
        return $user;
94
    }
95
96
    public function processChanges(AbstractUser $user, ?AbstractUser $previousUser): void
97
    {
98
        $this->encodePassword($user);
99
        if (!$previousUser) {
100
            $user->setEmailAddressVerified($this->initialEmailVerifiedState);
101
            if (!$this->initialEmailVerifiedState && $this->verifyEmailOnRegister) {
102
                $user->setEmailAddressVerifyToken($this->passwordEncoder->encodePassword($user, $token = TokenGenerator::generateToken()));
103
                $user->plainEmailAddressVerifyToken = $token;
104
            }
105
        }
106
107
        if ($previousUser) {
108
            if ($this->verifyEmailOnChange && $user->getEmailAddress() !== $previousUser->getEmailAddress()) {
109
                $user->setEmailAddressVerifyToken($this->passwordEncoder->encodePassword($user, $token = TokenGenerator::generateToken()));
110
                $user->plainEmailAddressVerifyToken = $token;
111
            }
112
            if (($newEmail = $user->getNewEmailAddress()) !== $previousUser->getNewEmailAddress()) {
113
                if ($newEmail) {
114
                    $user->setNewEmailConfirmationToken($this->passwordEncoder->encodePassword($user, $token = TokenGenerator::generateToken()));
115
                    $user->plainNewEmailConfirmationToken = $token;
116
                } else {
117
                    // invalidate any existing requests
118
                    $user->setNewEmailConfirmationToken(null);
119
                }
120
            }
121
        }
122
    }
123
124
    private function encodePassword(AbstractUser $entity): bool
125
    {
126
        if (!$entity->getPlainPassword()) {
127
            return false;
128
        }
129
        $encoded = $this->passwordEncoder->encodePassword($entity, $entity->getPlainPassword());
130
        $entity->setPassword($encoded);
131
        $entity->eraseCredentials();
132
133
        return true;
134
    }
135
}
136