Skip to content

When event_listeners_backward_compatibility_layer is disabled I can't pass URI variables into a Voter in security property. #5959

Description

@nickkadutskyi

API Platform version(s) affected: 3.2.3

Description
I have a resource where I pass a URI variable into a voter. But when I set event_listeners_backward_compatibility_layer to false and request GET /users/{user_id}/stats I get this error:

Variable "user_id" is not valid around position 87 for expression `is_granted('ROLE_ADMIN') or is_granted('ROLE_USER') and is_granted('USER_STATS_VIEW', user_id)`.

How to reproduce

  1. Have some resource with uriTemplate
  2. Set event_listeners_backward_compatibility_layer to false
  3. Pass uriTemplate to a voter in security property
  4. Get the error

Possible Solution
I think it has something to do with what providers are called after $uriVariables = $this->getOperationUriVariables... in ReadListener.php and in MainController.php

Additional Context

I do not know if that's by design so I kept event_listeners_backward_compatibility_layer set to false and now pass request variable to Voter and get user_id from it and this works fine.

Resource config for the original issue:

...
#[ApiResource(
    shortName: 'Stat',
    operations: [
        new Get(
            uriTemplate: '/users/{user_id}/stats/{id}.{_format}',
            uriVariables: [
                'user_id' => new Link(
                    toProperty: 'user',
                    fromClass: User::class,
                ),
                'id'      => new Link(
                    fromProperty: 'id',
                    fromClass: Stat::class
                ),
            ],
        ),
        new GetCollection(
            uriTemplate: '/users/{user_id}/stats.{_format}',
            uriVariables: [
                'user_id' => new Link(fromClass: User::class),
            ],
        )
    ],
    security: "is_granted('ROLE_ADMIN') or is_granted('ROLE_USER') and is_granted('USER_STATS_VIEW', user_id)",
    provider: StatsProvider::class
)]
class Stat
{
...

Stack Trace:

Symfony\Component\ExpressionLanguage\SyntaxError:
Variable "user_id" is not valid around position 87 for expression `is_granted('ROLE_ADMIN') or is_granted('ROLE_USER') and is_granted('USER_STATS_VIEW', user_id)`.

  at vendor/symfony/expression-language/Parser.php:249
  at Symfony\Component\ExpressionLanguage\Parser->parsePrimaryExpression()
     (vendor/symfony/expression-language/Parser.php:180)
  at Symfony\Component\ExpressionLanguage\Parser->getPrimary()
     (vendor/symfony/expression-language/Parser.php:138)
  at Symfony\Component\ExpressionLanguage\Parser->parseExpression()
     (vendor/symfony/expression-language/Parser.php:433)
  at Symfony\Component\ExpressionLanguage\Parser->parseArguments()
     (vendor/symfony/expression-language/Parser.php:245)
  at Symfony\Component\ExpressionLanguage\Parser->parsePrimaryExpression()
     (vendor/symfony/expression-language/Parser.php:180)
  at Symfony\Component\ExpressionLanguage\Parser->getPrimary()
     (vendor/symfony/expression-language/Parser.php:138)
  at Symfony\Component\ExpressionLanguage\Parser->parseExpression(16)
     (vendor/symfony/expression-language/Parser.php:144)
  at Symfony\Component\ExpressionLanguage\Parser->parseExpression(11)
     (vendor/symfony/expression-language/Parser.php:144)
  at Symfony\Component\ExpressionLanguage\Parser->parseExpression()
     (vendor/symfony/expression-language/Parser.php:123)
  at Symfony\Component\ExpressionLanguage\Parser->doParse(object(TokenStream), array('auth_checker', 'object', 'previous_object', 'request', 'roles', 'token', 'trust_resolver', 'user'))
     (vendor/symfony/expression-language/Parser.php:98)
  at Symfony\Component\ExpressionLanguage\Parser->parse(object(TokenStream), array('auth_checker', 'object', 'previous_object', 'request', 'roles', 'token', 'trust_resolver', 'user'))
     (vendor/symfony/expression-language/ExpressionLanguage.php:81)
  at Symfony\Component\ExpressionLanguage\ExpressionLanguage->parse('is_granted(\'ROLE_ADMIN\') or is_granted(\'ROLE_USER\') and is_granted(\'USER_STATS_VIEW\', user_id)', array('auth_checker', 'object', 'previous_object', 'request', 'roles', 'token', 'trust_resolver', 'user'))
     (vendor/symfony/expression-language/ExpressionLanguage.php:59)
  at Symfony\Component\ExpressionLanguage\ExpressionLanguage->evaluate('is_granted(\'ROLE_ADMIN\') or is_granted(\'ROLE_USER\') and is_granted(\'USER_STATS_VIEW\', user_id)', array('object' => array(object(Stat)), 'previous_object' => array(object(Stat)), 'request' => object(Request), 'trust_resolver' => object(AuthenticationTrustResolver), 'auth_checker' => object(AuthorizationChecker), 'token' => object(UsernamePasswordToken), 'user' => object(User), 'roles' => array('ROLE_ADMIN', 'ROLE_USER')))
     (vendor/api-platform/core/src/Symfony/Security/ResourceAccessChecker.php:56)
  at ApiPlatform\Symfony\Security\ResourceAccessChecker->isGranted('App\\ApiResource\\Stat', 'is_granted(\'ROLE_ADMIN\') or is_granted(\'ROLE_USER\') and is_granted(\'USER_STATS_VIEW\', user_id)', array('object' => array(object(Stat)), 'previous_object' => array(object(Stat)), 'request' => object(Request)))
     (vendor/api-platform/core/src/Symfony/Security/State/AccessCheckerProvider.php:78)
  at ApiPlatform\Symfony\Security\State\AccessCheckerProvider->provide(object(GetCollection), array('user_id' => 1), array('request' => object(Request), 'uri_variables' => array('user_id' => 1), 'resource_class' => 'App\\ApiResource\\Stat'))
     (vendor/api-platform/core/src/State/Provider/DeserializeProvider.php:47)
  at ApiPlatform\State\Provider\DeserializeProvider->provide(object(GetCollection), array('user_id' => 1), array('request' => object(Request), 'uri_variables' => array('user_id' => 1), 'resource_class' => 'App\\ApiResource\\Stat'))
     (vendor/api-platform/core/src/Symfony/Security/State/AccessCheckerProvider.php:53)
  at ApiPlatform\Symfony\Security\State\AccessCheckerProvider->provide(object(GetCollection), array('user_id' => 1), array('request' => object(Request), 'uri_variables' => array('user_id' => 1), 'resource_class' => 'App\\ApiResource\\Stat'))
     (vendor/api-platform/core/src/Symfony/Validator/State/QueryParameterValidateProvider.php:54)
  at ApiPlatform\Symfony\Validator\State\QueryParameterValidateProvider->provide(object(GetCollection), array('user_id' => 1), array('request' => object(Request), 'uri_variables' => array('user_id' => 1), 'resource_class' => 'App\\ApiResource\\Stat'))
     (vendor/api-platform/core/src/Symfony/Validator/State/ValidateProvider.php:32)
  at ApiPlatform\Symfony\Validator\State\ValidateProvider->provide(object(GetCollection), array('user_id' => 1), array('request' => object(Request), 'uri_variables' => array('user_id' => 1), 'resource_class' => 'App\\ApiResource\\Stat'))
     (vendor/api-platform/core/src/Symfony/Security/State/AccessCheckerProvider.php:53)
  at ApiPlatform\Symfony\Security\State\AccessCheckerProvider->provide(object(GetCollection), array('user_id' => 1), array('request' => object(Request), 'uri_variables' => array('user_id' => 1), 'resource_class' => 'App\\ApiResource\\Stat'))
     (vendor/api-platform/core/src/State/Provider/ContentNegotiationProvider.php:56)
  at ApiPlatform\State\Provider\ContentNegotiationProvider->provide(object(GetCollection), array('user_id' => 1), array('request' => object(Request), 'uri_variables' => array('user_id' => 1), 'resource_class' => 'App\\ApiResource\\Stat'))
     (vendor/api-platform/core/src/Symfony/Controller/MainController.php:74)
  at ApiPlatform\Symfony\Controller\MainController->__invoke(object(Request))
     (vendor/symfony/http-kernel/HttpKernel.php:181)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
     (vendor/symfony/http-kernel/HttpKernel.php:76)
  at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
     (vendor/symfony/http-kernel/Kernel.php:197)
  at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
     (vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
  at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
     (vendor/autoload_runtime.php:29)
  at require_once('/Users/nick/Developer/UPWZ/0000/job-search-api/vendor/autoload_runtime.php')
     (public/index.php:5)         

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions