Аутентификация в функциональных тестах в Symfony 2.1
В настоящее время я переношу проект 2.0. * на текущую 2.1 бета-версию Symfony.
В моих функциональных тестах я в настоящее время имею этот код для создания клиента с аутентификацией:
$client = // create a normal test client
$role = 'ROLE_USER';
$firewallName = 'main';
$user = // pull a user from db
$client->getCookieJar()->set(new \Symfony\Component\BrowserKit\Cookie(session_name(), true));
$token = new UsernamePasswordToken($user, null, $firewallName, array($role));
self::$kernel->getContainer()->get('session')->set('_security_' . $firewallName,
serialize($token));
это работает как ожидается в версии 2.0. *, но не в 2.1, данные не будут установлены в сеансе.
Любые идеи?
Изменить (добавить дополнительную информацию):
кажется, что проблема заключается в файле Symfony\Component\Security\Http\Firewall\ContextListener "в методе onKernelResponse". Существует этот код:
if ((null === $token = $this->context->getToken()) || ($token instanceof AnonymousToken)) {
$session->remove('_security_'.$this->contextKey);
} else {
$session->set('_security_'.$this->contextKey, serialize($token));
}
в моем случае if, если "$ token instanceof AnonymousToken" истинно, и из-за этого ключ сеанса удаляется. если я прокомментирую этот код, все работает так, как ожидалось.
Итак, я думаю, мой новый вопрос: что я могу сделать, чтобы маркер не был анонимным?
Ответы
Ответ 1
Правильный способ аутентификации пользователя:
$firewallName = 'your-firewall-name';
$container = self::$kernel->getContainer()
$token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles());
$container->get('security.context')->setToken($token);
и аутентификация брандмауэра:
$session = $container->get('session');
$session->set('_security_'.$firewallName, serialize($token));
$session->save();
Ответ 2
Я зарегистрировал билет: https://github.com/symfony/symfony/issues/3287
Кажется, что существует проблема с Symfony 2.1 RC.
Ответ 3
В моем testuite (работает с 2.0 и теперь 2.1) я использую базовый класс WebTestCase, который расширяет класс Symfony2 WebTestCase.
У меня есть две функции, которые создают анонимный клиент или зарегистрированный в моих тестах:
static protected function createClient(array $options = array(), array $server = array())
{
$client = parent::createClient($options, $server);
self::$container = self::$kernel->getContainer();
return $client;
}
protected function createAuthClient($user, $pass)
{
return self::createClient(array(), array(
'PHP_AUTH_USER' => $user,
'PHP_AUTH_PW' => $pass,
));
}
Затем в моих тестовых классах я использую:
$client = static::createClient();
для создания анонимного клиента и
$client = static::createAuthClient('user','pass');
чтобы создать аутентифицированный файл.
Это работает как шарм 2.1
Ответ 4
Я могу предложить другое решение (таким образом, он работает для меня). У меня есть собственный поставщик проверки подлинности со сложной логикой, и я не могу вообще использовать http_basic mechanic.
Вам нужно создать специальное действие, подобное этому
//TestAuthController.php
public function authTestAction(Request $request)
{
// you can add different security checks as you wish
$user_id = $request->query->get("id");
// find user by $user_id using service or user provider service from firewall config
$token = new UsernamePasswordToken($user, null, $firewallName, array($role));
// or another authenticated token
$this->container->get('security.context')->setToken($token);
}
добавить routing_test.yml и подключить его так же, как routing_dev.yml
Важно, что этот путь аутентификации должен существовать только в тестовой среде по соображениям безопасности.
//app/config/routing_test.yml
// ... the same routes as in routing_dev.yml
_testauth:
pattern: /__test
defaults: { _controller: MyBundle:TestAuth:authTest }
Затем в тесте просто отправьте клиент на этот URL
public function testSomething()
{
$client = static::createClient();
$client->request('GET','/__test',array('id'=>146));
$this->assertTrue($client->getContainer()->get('security.context')->isGranted('ROLE_USER')) //it will pass;
}
Ответ 5
У меня была такая же проблема и я нашел решение во время отладки. В зависимости от вашего хранилища сеансов вам может потребоваться изменить ваш файл cookie с session_name() на "MOCKSESSID" (который используется, если вы используете сеанс mock. Я думаю, что если в вашем файле config_test.yml есть следующее: оно автоматически изменяется на Mock Хранение сеансов:
framework:
test: ~
Для полноты, здесь мой полный код (я использую FOS/UserBundle)
protected function createAuthorizedClient($username = 'user') {
$client = $this->createClient(); //Normal WebTestCase client
$userProvider = $this->get('fos_user.user_manager');
$user = $userProvider->loadUserByUsername($username);
//$client->getCookieJar()->set(new Cookie(session_name(), true));
$client->getCookieJar()->set(new Cookie('MOCKSESSID', true));
$session = self::$kernel->getContainer()->get('session');
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$client->getContainer()->get('security.context')->setToken($token);
$session->set('_security_main', serialize($token));
return $client;
}