Passed
Pull Request — master (#72)
by Daniel
06:31
created

EmailAddressManager::confirmNewEmailAddress()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 21
nc 4
nop 3
dl 0
loc 31
ccs 0
cts 20
cp 0
crap 20
rs 9.584
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 Doctrine\ORM\EntityManagerInterface;
17
use Silverback\ApiComponentsBundle\EventListener\Api\UserEventListener;
18
use Silverback\ApiComponentsBundle\Exception\InvalidArgumentException;
19
use Silverback\ApiComponentsBundle\Exception\UnexpectedValueException;
20
use Silverback\ApiComponentsBundle\Repository\User\UserRepository;
21
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
22
23
/**
24
 * @author Daniel West <[email protected]>
25
 */
26
class EmailAddressManager
27
{
28
    private EntityManagerInterface $entityManager;
29
    private UserRepository $userRepository;
30
    private EncoderFactoryInterface $encoderFactory;
31
    private UserDataProcessor $userDataProcessor;
32
    private UserEventListener $userEventListener;
33
34
    public function __construct(
35
        EntityManagerInterface $entityManager,
36
        UserRepository $userRepository,
37
        EncoderFactoryInterface $encoderFactory,
38
        UserDataProcessor $userDataProcessor,
39
        UserEventListener $userEventListener
40
    ) {
41
        $this->entityManager = $entityManager;
42
        $this->userRepository = $userRepository;
43
        $this->encoderFactory = $encoderFactory;
44
        $this->userDataProcessor = $userDataProcessor;
45
        $this->userEventListener = $userEventListener;
46
    }
47
48
    public function confirmNewEmailAddress(string $username, string $email, string $token): void
49
    {
50
        $user = $this->userRepository->findOneByUsernameAndNewEmailAddress($username, $email);
51
        if (!$user) {
52
            throw new InvalidArgumentException('User not found');
53
        }
54
        $previousUser = clone $user;
55
        $encoder = $this->encoderFactory->getEncoder($user);
56
        if (!$encoder->isPasswordValid($user->getNewEmailConfirmationToken(), $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->getNewEmailConfirmationToken() 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

56
        if (!$encoder->isPasswordValid(/** @scrutinizer ignore-type */ $user->getNewEmailConfirmationToken(), $token, $user->getSalt())) {
Loading history...
57
            throw new InvalidArgumentException('Invalid token');
58
        }
59
60
        // Check if another user now exists with this new email address before persisting!
61
        $existingUser = $this->userRepository->findExistingUserByNewEmail($user);
62
        if ($existingUser) {
63
            $user
64
                ->setNewEmailAddress(null)
65
                ->setNewEmailConfirmationToken(null);
66
67
            $this->entityManager->flush();
68
            throw new UnexpectedValueException('Another user now exists with that email. Verification aborted.');
69
        }
70
71
        $user
72
            ->setEmailAddress($user->getNewEmailAddress())
73
            ->setNewEmailAddress(null)
74
            ->setEmailAddressVerified(false);
75
76
        $this->userDataProcessor->processChanges($user, $previousUser);
77
        $this->entityManager->flush();
78
        $this->userEventListener->postWrite($user, $previousUser);
79
    }
80
81
    public function verifyEmailAddress(string $username, string $token): void
82
    {
83
        $user = $this->userRepository->findOneBy([
84
            'username' => $username,
85
        ]);
86
        if (!$user) {
87
            throw new InvalidArgumentException('User not found');
88
        }
89
90
        $encoder = $this->encoderFactory->getEncoder($user);
91
        if (!$encoder->isPasswordValid($user->getEmailAddressVerifyToken(), $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->getEmailAddressVerifyToken() 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

91
        if (!$encoder->isPasswordValid(/** @scrutinizer ignore-type */ $user->getEmailAddressVerifyToken(), $token, $user->getSalt())) {
Loading history...
92
            throw new InvalidArgumentException('Invalid token');
93
        }
94
95
        $user->setEmailAddressVerified(true);
96
        $this->entityManager->flush();
97
    }
98
}
99