LockedUserSubscriber::getSubscribedEvents()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 0
dl 0
loc 13
ccs 12
cts 12
cp 1
crap 1
rs 9.9666
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * /src/EventSubscriber/LockedUserSubscriber.php
5
 *
6
 * @author TLe, Tarmo Leppänen <[email protected]>
7
 */
8
9
namespace App\EventSubscriber;
10
11
use App\Entity\LogLoginFailure;
12
use App\Entity\User;
13
use App\Repository\UserRepository;
14
use App\Resource\LogLoginFailureResource;
15
use App\Security\SecurityUser;
16
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationFailureEvent;
17
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
18
use Lexik\Bundle\JWTAuthenticationBundle\Events;
19
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\RequestStack;
22
use Symfony\Component\Security\Core\Exception\LockedException;
23
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
24
use Throwable;
25
use function assert;
26
use function count;
27
use function is_string;
28
29
/**
30
 * Class LockedUserSubscriber
31
 *
32
 * @package App\EventSubscriber
33
 * @author TLe, Tarmo Leppänen <[email protected]>
34
 */
35
class LockedUserSubscriber implements EventSubscriberInterface
36
{
37 22
    public function __construct(
38
        private readonly UserRepository $userRepository,
39
        private readonly LogLoginFailureResource $logLoginFailureResource,
40
        private readonly RequestStack $requestStack,
41
    ) {
42 22
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47 1
    public static function getSubscribedEvents(): array
48
    {
49 1
        return [
50 1
            AuthenticationSuccessEvent::class => [
51 1
                'onAuthenticationSuccess',
52 1
                128,
53 1
            ],
54 1
            Events::AUTHENTICATION_SUCCESS => [
55 1
                'onAuthenticationSuccess',
56 1
                128,
57 1
            ],
58 1
            AuthenticationFailureEvent::class => 'onAuthenticationFailure',
59 1
            Events::AUTHENTICATION_FAILURE => 'onAuthenticationFailure',
60 1
        ];
61
    }
62
63
    /**
64
     * @throws Throwable
65
     */
66 18
    public function onAuthenticationSuccess(AuthenticationSuccessEvent $event): void
67
    {
68 18
        $user = $this->getUser($event->getUser()) ?? throw new UnsupportedUserException('Unsupported user.');
69
70 17
        if (count($user->getLogsLoginFailure()) > 10) {
71 2
            throw new LockedException('Locked account.');
72
        }
73
74 15
        $this->logLoginFailureResource->reset($user);
75
    }
76
77
    /**
78
     * @throws Throwable
79
     */
80 5
    public function onAuthenticationFailure(): void
81
    {
82 5
        $request = $this->requestStack->getCurrentRequest();
83
84
        assert($request instanceof Request);
85
86 5
        $user = $this->getUser(
87 5
            (string)($request->query->get('username') ?? $request->request->get('username', ''))
88 5
        );
89
90 5
        if ($user !== null) {
91 3
            $this->logLoginFailureResource->save(new LogLoginFailure($user), true);
92
        }
93
    }
94
95
    /**
96
     * @throws Throwable
97
     */
98 22
    private function getUser(string | object $user): ?User
99
    {
100 22
        return match (true) {
101 22
            is_string($user) => $this->userRepository->loadUserByIdentifier($user, false),
0 ignored issues
show
Bug introduced by
It seems like $user can also be of type object; however, parameter $username of App\Repository\UserRepos...:loadUserByIdentifier() 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

101
            is_string($user) => $this->userRepository->loadUserByIdentifier(/** @scrutinizer ignore-type */ $user, false),
Loading history...
102 22
            $user instanceof SecurityUser =>
103 22
                $this->userRepository->loadUserByIdentifier($user->getUserIdentifier(), true),
104 22
            default => null,
105 22
        };
106
    }
107
}
108