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

api-platform / core / 19032144829

03 Nov 2025 10:56AM UTC coverage: 24.586%. First build
19032144829

Pull #7500

github

web-flow
Merge 19c5d0654 into 07d0ef86e
Pull Request #7500: feat(symfony): isGranted before provider

35 of 41 new or added lines in 3 files covered. (85.37%)

14114 of 57407 relevant lines covered (24.59%)

26.71 hits per line

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

85.71
/src/Symfony/Security/ResourceAccessChecker.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
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\Symfony\Security;
15

16
use ApiPlatform\Metadata\ResourceAccessCheckerInterface;
17
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
18
use Symfony\Component\ExpressionLanguage\Node\NameNode;
19
use Symfony\Component\ExpressionLanguage\Node\Node;
20
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
21
use Symfony\Component\Security\Core\Authentication\Token\NullToken;
22
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
23
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
24
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
25
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
26

27
/**
28
 * Checks if the logged user has sufficient permissions to access the given resource.
29
 *
30
 * @author Kévin Dunglas <dunglas@gmail.com>
31
 */
32
final class ResourceAccessChecker implements ResourceAccessCheckerInterface, ObjectVariableCheckerInterface
33
{
34
    public function __construct(private readonly ?ExpressionLanguage $expressionLanguage = null, private readonly ?AuthenticationTrustResolverInterface $authenticationTrustResolver = null, private readonly ?RoleHierarchyInterface $roleHierarchy = null, private readonly ?TokenStorageInterface $tokenStorage = null, private readonly ?AuthorizationCheckerInterface $authorizationChecker = null)
35
    {
36
    }
720✔
37

38
    public function isGranted(string $resourceClass, string $expression, array $extraVariables = []): bool
39
    {
40
        if (null === $this->tokenStorage || null === $this->authenticationTrustResolver) {
50✔
41
            throw new \LogicException('The "symfony/security" library must be installed to use the "security" attribute.');
2✔
42
        }
43

44
        if (null === $this->expressionLanguage) {
48✔
45
            throw new \LogicException('The "symfony/expression-language" library must be installed to use the "security" attribute.');
2✔
46
        }
47

48
        return (bool) $this->expressionLanguage->evaluate($expression, $this->getVariables($extraVariables));
46✔
49
    }
50

51
    public function usesObjectVariable(string $expression, array $variables = []): bool
52
    {
53
        return $this->hasObjectVariable($this->expressionLanguage->parse($expression, array_keys($this->getVariables($variables)))->getNodes()->toArray());
16✔
54
    }
55

56
    /**
57
     * @copyright Fabien Potencier <fabien@symfony.com>
58
     *
59
     * @see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php
60
     */
61
    private function getVariables(array $variables): array
62
    {
63
        if (null === $token = $this->tokenStorage->getToken()) {
46✔
64
            $token = new NullToken();
22✔
65
        }
66

67
        return array_merge($variables, [
46✔
68
            'token' => $token,
46✔
69
            'user' => $token->getUser(),
46✔
70
            'roles' => $this->getEffectiveRoles($token),
46✔
71
            'trust_resolver' => $this->authenticationTrustResolver,
46✔
72
            'auth_checker' => $this->authorizationChecker, // needed for the is_granted expression function
46✔
73
        ]);
46✔
74
    }
75

76
    /**
77
     * @return string[]
78
     */
79
    private function getEffectiveRoles(TokenInterface $token): array
80
    {
81
        if (null === $this->roleHierarchy) {
46✔
82
            return $token->getRoleNames();
6✔
83
        }
84

85
        return $this->roleHierarchy->getReachableRoleNames($token->getRoleNames());
40✔
86
    }
87

88
    /**
89
     * Recursively checks if a variable named 'object' is present in the expression AST.
90
     *
91
     * @param Node|array<mixed>|null $nodeOrNodes the ExpressionLanguage Node instance or an array of nodes/values
92
     */
93
    private function hasObjectVariable(Node|array|null $nodeOrNodes): bool
94
    {
95
        if ($nodeOrNodes instanceof NameNode) {
16✔
NEW
96
            if ('object' === $nodeOrNodes->attributes['name'] || 'previous_object' === $nodeOrNodes->attributes['name']) {
×
NEW
97
                return true;
×
98
            }
99

NEW
100
            return false;
×
101
        }
102

103
        if ($nodeOrNodes instanceof Node) {
16✔
104
            foreach ($nodeOrNodes->nodes as $childNode) {
16✔
105
                if ($this->hasObjectVariable($childNode)) {
8✔
NEW
106
                    return true;
×
107
                }
108
            }
109

110
            return false;
16✔
111
        }
112

113
        if (\is_array($nodeOrNodes)) {
16✔
114
            foreach ($nodeOrNodes as $element) {
16✔
115
                if (\is_string($element)) {
16✔
116
                    continue;
16✔
117
                }
118

119
                if ($this->hasObjectVariable($element)) {
16✔
NEW
120
                    return true;
×
121
                }
122
            }
123
        }
124

125
        return false;
16✔
126
    }
127
}
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

© 2025 Coveralls, Inc