Passing more than one Security attribute to AccessDecisionManager::decide() is not supported – Symfony 5
Что делать, если после обновления Symfony до версии 5.0.* вдруг стала появляться ошибка:
Passing more than one Security attribute to "Symfony\Component\Security\Core\Authorization\AccessDecisionManager::decide()" is not supported.
Испокон веков в Symfony можно было в config/packages/security.yaml указать сразу несколько ролей для того, чтобы ограничить доступ к конкретному разделу:
security:
access_control:
- { path: ^/, roles: [IS_AUTHENTICATED_ANONYMOUSLY, ROLE_USER] }
- { path: ^/some-secret-place, roles: [ROLE_MANAGER, ROLE_OPERATOR] }
Это позволяло дать доступ к одному и тому же разделу пользователям с разными ролями.
Но вот настало время, когда разработчики подумали: «Непонятно, что означает такой вид записи: "ROLE_MANAGER и ROLE_OPERATOR" или всё же "ROLE_MANAGER или ROLE_OPERATOR" 🤔» (пруф).
This PR deprecates passing more than one attribute to isGranted() and decide() to remove this confusing bit in Security usage.
Так вот начиная с версии Symfony 5.0.* (я поймал конкретно в версии 5.0.7, например) следует либо вовсе отказаться от такого вида записей, либо же использовать следующий синтаксис:
security:
access_control:
- { path: ^/, allow_if: "is_granted('IS_AUTHENTICATED_ANONYMOUSLY') or is_granted('ROLE_USER')"}
- { path: ^/some-secret-place, allow_if: "is_granted('ROLE_MANAGER') or is_granted('ROLE_OPERATOR')"}
Чтобы такой вид записи (c allow_if) точно работал, убедитесь, что у вас установлен этот компонент:
composer require symfony/expression-language
Сразу после установки этого компонента и корректировки записи в security.yaml всё должно заработать.
Или можно сделать то же самое с помощью иерархии ролей, если вы действительно хочите ограничить доступ именно таким образом:
role_hierarchy:
ROLE_EXAMPLE: [ROLE_MANAGER, ROLE_OPERATOR]
security:
access_control:
- { path: ^/some-secret-place, roles: ROLE_EXAMPLE}
Важно отметить, что если где-то в проекте вы используете в аннотациях записи вида:
/**
* Some useful function
*
* @IsGranted({"ROLE_MANAGER", "ROLE_OPERATOR"})
*
* @return JsonResponse
*/
public function doSomethingUseful(): JsonResponse
{
if ($this->isGranted(['ROLE_MANAGER', 'ROLE_OPERATOR'])) {
// do something
}
}
То тут также может возникнуть подобная ситуация. В таком случае следует использовать запись такого вида:
if ($this->isGranted("has_role('ROLE_MANAGER') or has_role('ROLE_OPERATOR')")) {
// ...
}
if ($this->isGranted('ROLE_MANAGER') || $this->isGranted('ROLE_OPERATOR')) {
// ...
}
Или вместо @IsGranted использовать аннотацию вида:
@Security("is_granted('ROLE_MANAGER') or is_granted('ROLE_OPERATOR')")