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

api-platform / core / 20847864477

09 Jan 2026 09:47AM UTC coverage: 29.1% (+0.005%) from 29.095%
20847864477

Pull #7649

github

web-flow
Merge b342dd5db into d640d106b
Pull Request #7649: feat(validator): uuid/ulid parameter validation

0 of 4 new or added lines in 1 file covered. (0.0%)

15050 existing lines in 491 files now uncovered.

16996 of 58406 relevant lines covered (29.1%)

81.8 hits per line

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

97.14
/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
    {
UNCOV
36
    }
2,419✔
37

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

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

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

51
    public function usesObjectVariable(string $expression, array $variables = []): bool
52
    {
UNCOV
53
        return $this->hasObjectVariable($this->expressionLanguage->parse($expression, array_keys($this->getVariables($variables)))->getNodes()->toArray());
75✔
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
    {
UNCOV
63
        if (null === $token = $this->tokenStorage->getToken()) {
184✔
UNCOV
64
            $token = new NullToken();
38✔
65
        }
66

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

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

UNCOV
85
        return $this->roleHierarchy->getReachableRoleNames($token->getRoleNames());
178✔
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
    {
UNCOV
95
        if ($nodeOrNodes instanceof NameNode) {
75✔
96
            if ('object' === $nodeOrNodes->attributes['name'] || 'previous_object' === $nodeOrNodes->attributes['name']) {
14✔
97
                return true;
14✔
98
            }
99

100
            return false;
×
101
        }
102

UNCOV
103
        if ($nodeOrNodes instanceof Node) {
75✔
UNCOV
104
            foreach ($nodeOrNodes->nodes as $childNode) {
75✔
UNCOV
105
                if ($this->hasObjectVariable($childNode)) {
38✔
106
                    return true;
14✔
107
                }
108
            }
109

UNCOV
110
            return false;
75✔
111
        }
112

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

UNCOV
119
                if ($this->hasObjectVariable($element)) {
75✔
120
                    return true;
14✔
121
                }
122
            }
123
        }
124

UNCOV
125
        return false;
61✔
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

© 2026 Coveralls, Inc