Test Failed
Push — master ( 9ceea8...5b7b5b )
by Daniel
09:29
created

DenyAccessListener::isComponentAllowedByIfPageDataIsReachableAnonymously()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 19
c 0
b 0
f 0
nc 5
nop 2
dl 0
loc 35
ccs 0
cts 21
cp 0
crap 30
rs 9.3222
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\Security\EventListener;
15
16
use Silverback\ApiComponentsBundle\Entity\Core\AbstractComponent;
17
use Silverback\ApiComponentsBundle\Entity\Core\AbstractPageData;
18
use Silverback\ApiComponentsBundle\Repository\Core\RouteRepository;
19
use Silverback\ApiComponentsBundle\Security\Voter\ComponentVoter;
20
use Silverback\ApiComponentsBundle\Security\Voter\RouteVoter;
21
use Symfony\Component\HttpKernel\Event\RequestEvent;
22
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
23
use Symfony\Component\Security\Core\Security;
24
25
/**
26
 * This will NOT restrict access to components fetched as a collection. As recommended by API Platform best practices, that should
27
 * be implemented in a Doctrine extension by the application developer.
28
 *
29
 * @author Daniel West <[email protected]>
30
 */
31
final class DenyAccessListener
32
{
33
    private Security $security;
34
    private RouteRepository $routeRepository;
35
36
    public function __construct(Security $security, RouteRepository $routeRepository)
37
    {
38
        $this->security = $security;
39
        $this->routeRepository = $routeRepository;
40
    }
41
42
    public function onPreDeserialize(RequestEvent $event): void
43
    {
44
        $request = $event->getRequest();
45
46
        $resource = $request->attributes->get('data');
47
48
        if ($resource instanceof AbstractComponent) {
49
            if ($this->security->isGranted(ComponentVoter::READ_COMPONENT, $resource)) {
50
                return;
51
            }
52
            throw new AccessDeniedException('Component access denied.');
53
        }
54
55
        if ($resource instanceof AbstractPageData) {
56
            if (false !== $this->isPageDataAllowedByRoute($resource)) {
57
                return;
58
            }
59
            throw new AccessDeniedException('Page data access denied.');
60
        }
61
    }
62
63
    private function isPageDataAllowedByRoute(AbstractPageData $pageData): ?bool
64
    {
65
        $routes = $this->routeRepository->findByPageData($pageData);
66
67
        // abstain - no route to check
68
        if (!\count($routes)) {
69
            return null;
70
        }
71
72
        foreach ($routes as $route) {
73
            $isGranted = $this->security->isGranted(RouteVoter::READ_ROUTE, $route);
74
            if ($isGranted) {
75
                return true;
76
            }
77
        }
78
79
        return false;
80
    }
81
}
82