Completed
Push — master ( 3c98fd...23d0f8 )
by Kévin
24s queued 12s
created

DenyAccessListener::onSecurity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[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 ApiPlatform\Core\Security\EventListener;
15
16
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
17
use ApiPlatform\Core\Security\ExpressionLanguage;
18
use ApiPlatform\Core\Security\ResourceAccessChecker;
19
use ApiPlatform\Core\Security\ResourceAccessCheckerInterface;
20
use ApiPlatform\Core\Util\RequestAttributesExtractor;
21
use Symfony\Component\HttpFoundation\Request;
22
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
23
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
24
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
25
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
26
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
27
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
28
29
/**
30
 * Denies access to the current resource if the logged user doesn't have sufficient permissions.
31
 *
32
 * @author Kévin Dunglas <[email protected]>
33
 */
34
final class DenyAccessListener
35
{
36
    private $resourceMetadataFactory;
37
    /**
38
     * @var ResourceAccessCheckerInterface
39
     */
40
    private $resourceAccessChecker;
41
42
    public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, /*ResourceAccessCheckerInterface*/ $resourceAccessCheckerOrExpressionLanguage = null, AuthenticationTrustResolverInterface $authenticationTrustResolver = null, RoleHierarchyInterface $roleHierarchy = null, TokenStorageInterface $tokenStorage = null, AuthorizationCheckerInterface $authorizationChecker = null)
43
    {
44
        $this->resourceMetadataFactory = $resourceMetadataFactory;
45
46
        if ($resourceAccessCheckerOrExpressionLanguage instanceof ResourceAccessCheckerInterface) {
47
            $this->resourceAccessChecker = $resourceAccessCheckerOrExpressionLanguage;
48
49
            return;
50
        }
51
52
        $this->resourceAccessChecker = new ResourceAccessChecker($resourceAccessCheckerOrExpressionLanguage, $authenticationTrustResolver, $roleHierarchy, $tokenStorage, $authorizationChecker);
53
        @trigger_error(sprintf('Passing an instance of "%s" or null as second argument of "%s" is deprecated since API Platform 2.2 and will not be possible anymore in API Platform 3. Pass an instance of "%s" and no extra argument instead.', ExpressionLanguage::class, self::class, ResourceAccessCheckerInterface::class), E_USER_DEPRECATED);
54
    }
55
56
    public function onKernelRequest(GetResponseEvent $event): void
57
    {
58
        @trigger_error(sprintf('Method "%1$s::onKernelRequest" is deprecated since API Platform 2.4 and will not be available anymore in API Platform 3. Prefer calling "%1$s::onSecurity" instead.', self::class), E_USER_DEPRECATED);
59
        $this->onSecurityPostDenormalize($event);
60
    }
61
62
    public function onSecurity(GetResponseEvent $event): void
63
    {
64
        $this->checkSecurity($event->getRequest(), 'security', false);
65
    }
66
67
    public function onSecurityPostDenormalize(GetResponseEvent $event): void
68
    {
69
        $request = $event->getRequest();
70
        $this->checkSecurity($request, 'security_post_denormalize', true, [
71
            'previous_object' => $request->attributes->get('previous_data'),
72
        ]);
73
    }
74
75
    /**
76
     * @throws AccessDeniedException
77
     */
78
    private function checkSecurity(Request $request, string $attribute, bool $backwardCompatibility, array $extraVariables = []): void
79
    {
80
        if (!$attributes = RequestAttributesExtractor::extractAttributes($request)) {
81
            return;
82
        }
83
84
        $resourceMetadata = $this->resourceMetadataFactory->create($attributes['resource_class']);
85
86
        $isGranted = $resourceMetadata->getOperationAttribute($attributes, $attribute, null, true);
87
        if ($backwardCompatibility && null === $isGranted) {
88
            // Backward compatibility
89
            $isGranted = $resourceMetadata->getOperationAttribute($attributes, 'access_control', null, true);
90
            if (null !== $isGranted) {
91
                @trigger_error('Using "access_control" attribute is deprecated since API Platform 2.4 and will not be possible anymore in API Platform 3. Use "security" attribute instead.', E_USER_DEPRECATED);
92
            }
93
        }
94
95
        if (null === $isGranted) {
96
            return;
97
        }
98
99
        $extraVariables += $request->attributes->all();
100
        $extraVariables['object'] = $request->attributes->get('data');
101
        $extraVariables['request'] = $request;
102
103
        if (!$this->resourceAccessChecker->isGranted($attributes['resource_class'], $isGranted, $extraVariables)) {
104
            throw new AccessDeniedException($resourceMetadata->getOperationAttribute($attributes, $attribute.'_message', 'Access Denied.', true));
105
        }
106
    }
107
}
108