• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tarlepp / symfony-flex-backend / #6191

14 May 2026 11:44AM UTC coverage: 99.875% (-0.1%) from 100.0%
#6191

push

php-coveralls

web-flow
Merge pull request #3273 from tarlepp/chore(deps)/dependency-update

Chore(deps) - Dependency update

2392 of 2395 relevant lines covered (99.87%)

88.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.43
/src/EventSubscriber/LockedUserSubscriber.php
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * /src/EventSubscriber/LockedUserSubscriber.php
5
 *
6
 * @author TLe, Tarmo Leppänen <tarmo.leppanen@pinja.com>
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 Override;
20
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
21
use Symfony\Component\HttpFoundation\Request;
22
use Symfony\Component\HttpFoundation\RequestStack;
23
use Symfony\Component\Security\Core\Exception\LockedException;
24
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
25
use Throwable;
26
use function assert;
27
use function count;
28
use function is_string;
29

30
/**
31
 * @package App\EventSubscriber
32
 * @author TLe, Tarmo Leppänen <tarmo.leppanen@pinja.com>
33
 */
34
class LockedUserSubscriber implements EventSubscriberInterface
35
{
36
    public function __construct(
37
        private readonly UserRepository $userRepository,
38
        private readonly LogLoginFailureResource $logLoginFailureResource,
39
        private readonly RequestStack $requestStack,
40
    ) {
41
    }
22✔
42

43
    /**
44
     * {@inheritdoc}
45
     */
46
    #[Override]
47
    public static function getSubscribedEvents(): array
48
    {
49
        return [
1✔
50
            AuthenticationSuccessEvent::class => [
1✔
51
                'onAuthenticationSuccess',
1✔
52
                128,
1✔
53
            ],
1✔
54
            Events::AUTHENTICATION_SUCCESS => [
1✔
55
                'onAuthenticationSuccess',
1✔
56
                128,
1✔
57
            ],
1✔
58
            AuthenticationFailureEvent::class => 'onAuthenticationFailure',
1✔
59
            Events::AUTHENTICATION_FAILURE => 'onAuthenticationFailure',
1✔
60
        ];
1✔
61
    }
62

63
    /**
64
     * @throws Throwable
65
     */
66
    public function onAuthenticationSuccess(AuthenticationSuccessEvent $event): void
67
    {
68
        $user = $this->getUser($event->getUser()) ?? throw new UnsupportedUserException('Unsupported user.');
18✔
69

70
        if (count($user->getLogsLoginFailure()) > 10) {
17✔
71
            throw new LockedException('Locked account.');
2✔
72
        }
73

74
        $this->logLoginFailureResource->reset($user);
15✔
75
    }
76

77
    /**
78
     * @throws Throwable
79
     */
80
    public function onAuthenticationFailure(): void
81
    {
82
        $request = $this->requestStack->getCurrentRequest();
5✔
83

84
        assert($request instanceof Request);
×
85

86
        $user = $this->getUser(
5✔
87
            (string)($request->query->get('username') ?? $request->request->get('username', ''))
5✔
88
        );
5✔
89

90
        if ($user !== null) {
5✔
91
            $this->logLoginFailureResource->save(new LogLoginFailure($user), true);
3✔
92
        }
93
    }
94

95
    /**
96
     * @throws Throwable
97
     */
98
    private function getUser(string | object $user): ?User
99
    {
100
        return match (true) {
101
            is_string($user) => $this->userRepository->loadUserByIdentifier($user, false),
22✔
102
            $user instanceof SecurityUser =>
18✔
103
                $this->userRepository->loadUserByIdentifier($user->getUserIdentifier(), true),
17✔
104
            default => null,
22✔
105
        };
106
    }
107
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc