Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
24 / 24
94.74% covered (success)
94.74%
18 / 19
92.31% covered (success)
92.31%
12 / 13
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
AuthRateLimitSubscriber
100.00% covered (success)
100.00%
24 / 24
94.74% covered (success)
94.74%
18 / 19
92.31% covered (success)
92.31%
12 / 13
100.00% covered (success)
100.00%
6 / 6
11.06
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isMainRequest
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 routeShouldBeLimited
100.00% covered (success)
100.00%
6 / 6
83.33% covered (warning)
83.33%
5 / 6
66.67% covered (warning)
66.67%
2 / 3
100.00% covered (success)
100.00%
1 / 1
2.15
 limitShouldBeApplied
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 onKernelController
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 getSubscribedEvents
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Auth\Infrastructure\EventSubscriber;
4
5use Symfony\Bundle\SecurityBundle\Security;
6use Symfony\Component\EventDispatcher\EventSubscriberInterface;
7use Symfony\Component\HttpFoundation\Request;
8use Symfony\Component\HttpKernel\Event\ControllerEvent;
9use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
10use Symfony\Component\HttpKernel\HttpKernelInterface;
11use Symfony\Component\HttpKernel\KernelEvents;
12use Symfony\Component\RateLimiter\RateLimiterFactoryInterface;
13
14/**
15 * This rate limit applies to the Identity 'slice' only
16 * Other slices may have their own subscribers (or not)
17 * TODO : maybe we should consider the rate limit a global feature instead or relying on each slice to implement it
18 * Although it may seem 'cleaner' to have each slice implement it, it may result in code duplication
19 * Moreover, should slices become isolated micro services, the rate limiting would probably be handled differently
20 * We should make a decision, but it's not that important at the moment
21 *
22 * @author Wilhelm Zwertvaegher
23 *
24 */
25readonly class AuthRateLimitSubscriber implements EventSubscriberInterface
26{
27    public function __construct(
28        private Security                    $security,
29        private RateLimiterFactoryInterface $registerByIpLimiter,
30        private RateLimiterFactoryInterface $apiByUserLimiter
31    ) {
32    }
33
34    public function isMainRequest(ControllerEvent $event): bool
35    {
36        return $event->getRequestType() === HttpKernelInterface::MAIN_REQUEST;
37    }
38
39    public function routeShouldBeLimited(Request $request): bool
40    {
41        $routesToLimit = [
42            'api_auth_registration',
43            'api_auth_me'
44        ];
45        $route = $request->attributes->get('_route');
46        return $route && in_array($route, $routesToLimit, true);
47    }
48
49    public function limitShouldBeApplied(ControllerEvent $event): bool
50    {
51        return $this->isMainRequest($event) && $this->routeShouldBeLimited($event->getRequest());
52    }
53
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
57            return;
58        }
59
60        $request = $event->getRequest();
61        $user = $this->security->getUser();
62
63        $factory = $this->registerByIpLimiter;
64        $key = $request->getClientIp();
65        if ($user) {
66            $factory = $this->apiByUserLimiter;
67            $key = $user->getUserIdentifier();
68        }
69
70        $limiter = $factory->create($key);
71        if (!$limiter->consume()->isAccepted()) {
72            throw new TooManyRequestsHttpException();
73        }
74    }
75
76    public static function getSubscribedEvents(): array
77    {
78        return [
79            KernelEvents::CONTROLLER => 'onKernelController',
80        ];
81    }
82}

Paths

Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

AuthRateLimitSubscriber->__construct
27    public function __construct(
28        private Security                    $security,
29        private RateLimiterFactoryInterface $registerByIpLimiter,
30        private RateLimiterFactoryInterface $apiByUserLimiter
31    ) {
32    }
AuthRateLimitSubscriber->getSubscribedEvents
79            KernelEvents::CONTROLLER => 'onKernelController',
80        ];
81    }
AuthRateLimitSubscriber->isMainRequest
34    public function isMainRequest(ControllerEvent $event): bool
35    {
36        return $event->getRequestType() === HttpKernelInterface::MAIN_REQUEST;
37    }
AuthRateLimitSubscriber->limitShouldBeApplied
49    public function limitShouldBeApplied(ControllerEvent $event): bool
50    {
51        return $this->isMainRequest($event) && $this->routeShouldBeLimited($event->getRequest());
 
51        return $this->isMainRequest($event) && $this->routeShouldBeLimited($event->getRequest());
 
51        return $this->isMainRequest($event) && $this->routeShouldBeLimited($event->getRequest());
52    }
49    public function limitShouldBeApplied(ControllerEvent $event): bool
50    {
51        return $this->isMainRequest($event) && $this->routeShouldBeLimited($event->getRequest());
 
51        return $this->isMainRequest($event) && $this->routeShouldBeLimited($event->getRequest());
52    }
AuthRateLimitSubscriber->onKernelController
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
 
57            return;
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
 
60        $request = $event->getRequest();
61        $user = $this->security->getUser();
62
63        $factory = $this->registerByIpLimiter;
64        $key = $request->getClientIp();
65        if ($user) {
 
66            $factory = $this->apiByUserLimiter;
67            $key = $user->getUserIdentifier();
68        }
69
70        $limiter = $factory->create($key);
 
70        $limiter = $factory->create($key);
71        if (!$limiter->consume()->isAccepted()) {
 
72            throw new TooManyRequestsHttpException();
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
 
60        $request = $event->getRequest();
61        $user = $this->security->getUser();
62
63        $factory = $this->registerByIpLimiter;
64        $key = $request->getClientIp();
65        if ($user) {
 
66            $factory = $this->apiByUserLimiter;
67            $key = $user->getUserIdentifier();
68        }
69
70        $limiter = $factory->create($key);
 
70        $limiter = $factory->create($key);
71        if (!$limiter->consume()->isAccepted()) {
 
74    }
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
 
60        $request = $event->getRequest();
61        $user = $this->security->getUser();
62
63        $factory = $this->registerByIpLimiter;
64        $key = $request->getClientIp();
65        if ($user) {
 
70        $limiter = $factory->create($key);
71        if (!$limiter->consume()->isAccepted()) {
 
72            throw new TooManyRequestsHttpException();
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
 
60        $request = $event->getRequest();
61        $user = $this->security->getUser();
62
63        $factory = $this->registerByIpLimiter;
64        $key = $request->getClientIp();
65        if ($user) {
 
70        $limiter = $factory->create($key);
71        if (!$limiter->consume()->isAccepted()) {
 
74    }
AuthRateLimitSubscriber->routeShouldBeLimited
39    public function routeShouldBeLimited(Request $request): bool
40    {
41        $routesToLimit = [
42            'api_auth_registration',
43            'api_auth_me'
44        ];
45        $route = $request->attributes->get('_route');
46        return $route && in_array($route, $routesToLimit, true);
 
46        return $route && in_array($route, $routesToLimit, true);
 
46        return $route && in_array($route, $routesToLimit, true);
 
46        return $route && in_array($route, $routesToLimit, true);
 
46        return $route && in_array($route, $routesToLimit, true);
47    }
39    public function routeShouldBeLimited(Request $request): bool
40    {
41        $routesToLimit = [
42            'api_auth_registration',
43            'api_auth_me'
44        ];
45        $route = $request->attributes->get('_route');
46        return $route && in_array($route, $routesToLimit, true);
 
46        return $route && in_array($route, $routesToLimit, true);
 
46        return $route && in_array($route, $routesToLimit, true);
 
46        return $route && in_array($route, $routesToLimit, true);
 
46        return $route && in_array($route, $routesToLimit, true);
47    }
39    public function routeShouldBeLimited(Request $request): bool
40    {
41        $routesToLimit = [
42            'api_auth_registration',
43            'api_auth_me'
44        ];
45        $route = $request->attributes->get('_route');
46        return $route && in_array($route, $routesToLimit, true);
 
46        return $route && in_array($route, $routesToLimit, true);
47    }
onKernelController
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
 
57            return;
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
 
60        $request = $event->getRequest();
61        $user = $this->security->getUser();
62
63        $factory = $this->registerByIpLimiter;
64        $key = $request->getClientIp();
65        if ($user) {
 
66            $factory = $this->apiByUserLimiter;
67            $key = $user->getUserIdentifier();
68        }
69
70        $limiter = $factory->create($key);
 
70        $limiter = $factory->create($key);
71        if (!$limiter->consume()->isAccepted()) {
 
72            throw new TooManyRequestsHttpException();
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
 
60        $request = $event->getRequest();
61        $user = $this->security->getUser();
62
63        $factory = $this->registerByIpLimiter;
64        $key = $request->getClientIp();
65        if ($user) {
 
66            $factory = $this->apiByUserLimiter;
67            $key = $user->getUserIdentifier();
68        }
69
70        $limiter = $factory->create($key);
 
70        $limiter = $factory->create($key);
71        if (!$limiter->consume()->isAccepted()) {
 
74    }
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
 
60        $request = $event->getRequest();
61        $user = $this->security->getUser();
62
63        $factory = $this->registerByIpLimiter;
64        $key = $request->getClientIp();
65        if ($user) {
 
70        $limiter = $factory->create($key);
71        if (!$limiter->consume()->isAccepted()) {
 
72            throw new TooManyRequestsHttpException();
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
 
60        $request = $event->getRequest();
61        $user = $this->security->getUser();
62
63        $factory = $this->registerByIpLimiter;
64        $key = $request->getClientIp();
65        if ($user) {
 
70        $limiter = $factory->create($key);
71        if (!$limiter->consume()->isAccepted()) {
 
74    }
{main}
3namespace App\Auth\Infrastructure\EventSubscriber;
4
5use Symfony\Bundle\SecurityBundle\Security;
6use Symfony\Component\EventDispatcher\EventSubscriberInterface;
7use Symfony\Component\HttpFoundation\Request;
8use Symfony\Component\HttpKernel\Event\ControllerEvent;
9use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
10use Symfony\Component\HttpKernel\HttpKernelInterface;
11use Symfony\Component\HttpKernel\KernelEvents;
12use Symfony\Component\RateLimiter\RateLimiterFactoryInterface;
13
14/**
15 * This rate limit applies to the Identity 'slice' only
16 * Other slices may have their own subscribers (or not)
17 * TODO : maybe we should consider the rate limit a global feature instead or relying on each slice to implement it
18 * Although it may seem 'cleaner' to have each slice implement it, it may result in code duplication
19 * Moreover, should slices become isolated micro services, the rate limiting would probably be handled differently
20 * We should make a decision, but it's not that important at the moment
21 *
22 * @author Wilhelm Zwertvaegher
23 *
24 */
25readonly class AuthRateLimitSubscriber implements EventSubscriberInterface
26{
27    public function __construct(
28        private Security                    $security,
29        private RateLimiterFactoryInterface $registerByIpLimiter,
30        private RateLimiterFactoryInterface $apiByUserLimiter
31    ) {
32    }
33
34    public function isMainRequest(ControllerEvent $event): bool
35    {
36        return $event->getRequestType() === HttpKernelInterface::MAIN_REQUEST;
37    }
38
39    public function routeShouldBeLimited(Request $request): bool
40    {
41        $routesToLimit = [
42            'api_auth_registration',
43            'api_auth_me'
44        ];
45        $route = $request->attributes->get('_route');
46        return $route && in_array($route, $routesToLimit, true);
47    }
48
49    public function limitShouldBeApplied(ControllerEvent $event): bool
50    {
51        return $this->isMainRequest($event) && $this->routeShouldBeLimited($event->getRequest());
52    }
53
54    public function onKernelController(ControllerEvent $event): void
55    {
56        if (!$this->limitShouldBeApplied($event)) {
57            return;
58        }
59
60        $request = $event->getRequest();
61        $user = $this->security->getUser();
62
63        $factory = $this->registerByIpLimiter;
64        $key = $request->getClientIp();
65        if ($user) {
66            $factory = $this->apiByUserLimiter;
67            $key = $user->getUserIdentifier();
68        }
69
70        $limiter = $factory->create($key);
71        if (!$limiter->consume()->isAccepted()) {
72            throw new TooManyRequestsHttpException();
73        }
74    }
75
76    public static function getSubscribedEvents(): array
77    {
78        return [
79            KernelEvents::CONTROLLER => 'onKernelController',
80        ];
81    }