Completed
Push — master ( da004e...0c3139 )
by Antoine
16s
created

DenyAccessListener::onKernelRequest()   D

Complexity

Conditions 10
Paths 19

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 21
nc 19
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Util\RequestAttributesExtractor;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
21
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
22
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
23
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
24
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
25
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
26
27
/**
28
 * Denies access to the current resource if the logged user doesn't have sufficient permissions.
29
 *
30
 * @author Kévin Dunglas <[email protected]>
31
 * @author Fabien Potencier <[email protected]>
32
 */
33
final class DenyAccessListener
34
{
35
    private $resourceMetadataFactory;
36
    private $expressionLanguage;
37
    private $authenticationTrustResolver;
38
    private $roleHierarchy;
39
    private $tokenStorage;
40
    private $authorizationChecker;
41
42
    public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, ExpressionLanguage $expressionLanguage = null, AuthenticationTrustResolverInterface $authenticationTrustResolver = null, RoleHierarchyInterface $roleHierarchy = null, TokenStorageInterface $tokenStorage = null, AuthorizationCheckerInterface $authorizationChecker = null)
43
    {
44
        $this->resourceMetadataFactory = $resourceMetadataFactory;
45
        $this->expressionLanguage = $expressionLanguage;
46
        $this->authenticationTrustResolver = $authenticationTrustResolver;
47
        $this->roleHierarchy = $roleHierarchy;
48
        $this->tokenStorage = $tokenStorage;
49
        $this->authorizationChecker = $authorizationChecker;
50
    }
51
52
    /**
53
     * Sets the applicable format to the HttpFoundation Request.
54
     *
55
     * @param GetResponseEvent $event
56
     *
57
     * @throws AccessDeniedException
58
     */
59
    public function onKernelRequest(GetResponseEvent $event)
60
    {
61
        $request = $event->getRequest();
62
        if (!$attributes = RequestAttributesExtractor::extractAttributes($request)) {
63
            return;
64
        }
65
66
        $resourceMetadata = $this->resourceMetadataFactory->create($attributes['resource_class']);
67
        if (isset($attributes['collection_operation_name'])) {
68
            $isGranted = $resourceMetadata->getCollectionOperationAttribute($attributes['collection_operation_name'], 'is_granted', null, true);
69
        } elseif (isset($attributes['item_operation_name'])) {
70
            $isGranted = $resourceMetadata->getItemOperationAttribute($attributes['item_operation_name'], 'is_granted', null, true);
71
        } else {
72
            $isGranted = $resourceMetadata->getCollectionOperationAttribute($attributes['subresource_operation_name'], 'is_granted', null, true);
73
        }
74
75
        if (null === $isGranted) {
76
            return;
77
        }
78
79
        if (null === $this->tokenStorage || null === $this->authenticationTrustResolver) {
80
            throw new \LogicException(sprintf('The "symfony/security" library must be installed to use the "is_granted" attribute on class "%s".', $attributes['resource_class']));
81
        }
82
        if (null === $this->tokenStorage->getToken()) {
83
            throw new \LogicException(sprintf('The resource must be behind a firewall to use the "is_granted" attribute on class "%s".', $attributes['resource_class']));
84
        }
85
        if (null === $this->expressionLanguage) {
86
            throw new \LogicException(sprintf('The "symfony/expression-language" library must be installed to use the "is_granted" attribute on class "%s".', $attributes['resource_class']));
87
        }
88
89
        if (!$this->expressionLanguage->evaluate($isGranted, $this->getVariables($request))) {
90
            throw new AccessDeniedException();
91
        }
92
    }
93
94
    /**
95
     * @copyright Fabien Potencier <[email protected]>
96
     *
97
     * @see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php
98
     */
99
    private function getVariables(Request $request): array
100
    {
101
        $token = $this->tokenStorage->getToken();
102
        $roles = $this->roleHierarchy ? $this->roleHierarchy->getReachableRoles($token->getRoles()) : $token->getRoles();
103
104
        $variables = [
105
            'token' => $token,
106
            'user' => $token->getUser(),
107
            'object' => $request->attributes->get('data'),
108
            'request' => $request,
109
            'roles' => array_map(function ($role) {return $role->getRole(); }, $roles),
110
            'trust_resolver' => $this->authenticationTrustResolver,
111
            // needed for the is_granted expression function
112
            'auth_checker' => $this->authorizationChecker,
113
        ];
114
115
        // controller variables should also be accessible
116
        return array_merge($request->attributes->all(), $variables);
117
    }
118
}
119