codeception functional Авторизация (symfony) через loginUser() не работает после первого запроса в функциональных тестах
Primary tabs
Проблема
Решение через loginUser() для программного логина пользователя, почему-то не работает у нас (symfony 7) в ситуации, когда в цепочке более двух тестов, например :
/**
* @before authAsSimpleUserVasya // первая авторизация
этот метода успешно создает статью
*/
public function testPostCreatingBySimpleUserVasya(FunctionalTester $I)
{
$client = $this->client;
$client->request(
'POST',
'/api/' . $this->getApiVersion() . '/post/new',
// .........
}
/**
Зависим от метода создания статьи, который перед собой
проводит авторизацию и все работет нормально
* @before testPostCreatingBySimpleUserVasya
* @before authAsAdmin // метода ниже не сработает
-- даже если авторизуемся еще раз
*/
public function canEditAndDeleteByAdminArticleCreatedByOtherSimpleUser(FunctionalTester $I)
{
$client = $this->client;
$client->request( // получаем ошибку
'PUT',
'/api/' . $this->getApiVersion() . '/post/edit/' . $this->postId,
// .......................
}
}- в нашем случае второй метода в цепоке (зависящий от первого через @before) не проходит проверку на роль IS_GRANTED (symfony) (напр. как в примерах кода тут)
Причина проблемы, почему пропадает авторизация
After making a request, subsequent requests will make the client reboot the kernel.
- то есть ядро перезагружается на каждый запрос (для файевроволов без состояния), и особенность тут в том, что оно перезагружается не после запроса, а перед выполнением следующего запроса - внутри $client->request(), именно поэтому просто "залогиниться" перед каждым запросом нельзя, т.к. симфони отслеживает, именно факт предыдущего запроса и тупо пересоздает ядро, что не делает повторную авторизацию перед вызовом метода запроса действенной.
Ключевые слова по теме (поисковые запросы):
symfony loginUser fail auth after one request
Возможно связанные темы
- KernelBrowser::loginUser does not work when called multiple times https://github.com/symfony/symfony/issue...
- KernelBrowser reinitializes TokenStorage on subesequent Request calls https://github.com/symfony/symfony/issue...
- Multiple Requests in One Test Несколько запросов в тестах (официальная документация): https://symfony.com/doc/current/testing....
Возможные решения
- Применять способ (если он там есть, надо вникать): https://symfony.com/doc/current/testing....
- Использовать костыль с перезагрузкой ядра, чтобы для второго запроса оно было "как для первого"
- Log in to post comments
- 1282 reads
vedro-compota
Fri, 04/05/2024 - 01:01
Permalink
где происходит проверка IS_GRANTED
Вот тут: vendor/symfony/security-core/Authorization/AuthorizationChecker.php
/** * AuthorizationChecker is the main authorization point of the Security component. * * It gives access to the token representing the current user authentication. * * @author Fabien Potencier <fabien @symfony.com> * @author Johannes M. Schmitt <schmittjoh @gmail.com> */ class AuthorizationChecker implements AuthorizationCheckerInterface { public function __construct( private TokenStorageInterface $tokenStorage, private AccessDecisionManagerInterface $accessDecisionManager, ) { } final public function isGranted(mixed $attribute, mixed $subject = null): bool { $token = $this->tokenStorage->getToken(); // сюда точку останова_____________
матфак вгу и остальная классика =)
vedro-compota
Fri, 04/05/2024 - 01:23
Permalink
Перезагрузка ядра
Функция перезагрузи это
Дла поиска:
_____________
матфак вгу и остальная классика =)
vedro-compota
Fri, 04/05/2024 - 02:53
Permalink
Возможное решение: все запросы как "первые"
Вариант с пересозданием (перелогином) каждвый раз перед вызовом с подходом:
Как предлагают тут https://github.com/symfony/symfony/issue...
Другое дело, что что если унаследовать Cest-класс от WebTestCase - начнет запускаться куча публичных методов в родителях, а это не то, что нам нужно)
Поэтому можно просто написать отдельный класс-наследние, который будет отдавать клиент с залогиненным пользователем:
use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\Security\Core\User\UserInterface; /** * Костыль для получения клиента с залогиненным пользователем * с предварительной (что важно) перезагрузкой ядра */ use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\Security\Core\User\UserInterface; /** * Костыль для получения клиента с залогиненным пользователем * с предварительной (что важно) перезагрузкой ядра */ class ClientGettingCrutch extends WebTestCase { /** * @param UserInterface $user Пользователь, под которым залогиним клиент */ public static function get(UserInterface $user): KernelBrowser { self::ensureKernelShutdown(); return self::createClient()->loginUser($user); } }-- просто получайте заново клиент перед каждым запросом и stateless: true файервол начнет пропускать не только первый запрос
Если в тестах вам нужно в тестах получать исключения, то выключить их перехват клиентом:
class ClientGettingCrutch extends WebTestCase { /** * @param UserInterface $user Пользователь, под которым залогиним клиент */ public static function get(UserInterface $user): KernelBrowser { self::ensureKernelShutdown(); $client = self::createClient()->loginUser($user); $client->catchExceptions(false); // выключаем перехват return $client; }_____________
матфак вгу и остальная классика =)
vedro-compota
Mon, 05/12/2025 - 23:18
Permalink
Исходный план, актуальное
Исходный план, актуальное решение см. в основном тексте выше.
План поиска причины проблемы
_____________
матфак вгу и остальная классика =)