I have entity service and I create controller and action get, create, edit and deleted. I look for test and I don't know why I have error? I can enter for this rout and have data, and work fine, but if create client and get status code have 302
But when I comment in security.yml
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
#- { path: ^/admin, roles: ROLE_ADMIN }
test passed almost all only in the late fall
How to create client with ROLE_ADMIN ??
and this test
public function testCompleteScenario()
{
// Create a new client to browse the application
$client = static::createClient();
// Create a new entry in the database
$crawler = $client->request('GET', '/admin/services/');
$this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /admin/services/");
$crawler = $client->click($crawler->selectLink('Create a new entry')->link());
// Fill in the form and submit it
$form = $crawler->selectButton('Create')->form(array(
'artel_profilebundle_services[services]' => 'Test',
// ... other fields to fill
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check data in the show view
$this->assertGreaterThan(0, $crawler->filter('td:contains("Test")')->count(), 'Missing element td:contains("Test")');
// Edit the entity
$crawler = $client->click($crawler->selectLink('Edit')->link());
$form = $crawler->selectButton('Update')->form(array(
'artel_profilebundle_services[services]' => 'Foo',
// ... other fields to fill
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check the element contains an attribute with value equals "Foo"
$this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), 'Missing element [value="Foo"]');
// Delete the entity
$client->submit($crawler->selectButton('Delete')->form());
$crawler = $client->followRedirect();
// Check the entity has been delete on the list
// **this is 51 line**
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent());
}
and I have
' does not match PCRE pattern "/Foo/".
/home/ivan/host/test/src/Artel/AdminBundle/Tests/Controller/ServicesControllerTest.php:51
where error ?
UPDATE
change
class ServicesControllerTest extends WebTestCase
{
private $client = null;
public function setUp()
{
$this->client = static::createClient();
}
public function logIn()
{
$session = $this->client->getContainer()->get('session');
$firewall = 'default';
$token = new UsernamePasswordToken('admin', null, $firewall, array('ROLE_ADMIN'));
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
public function testCompleteScenario()
{
// Create a new client to browse the application
$this->logIn();
// Create a new entry in the database
$crawler = $this->client->request('GET', '/admin/services/');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /admin/services/");
$crawler = $this->client->click($crawler->selectLink('Create a new entry')->link());
// Fill in the form and submit it
$form = $crawler->selectButton('Create')->form(array(
'artel_profilebundle_services[services]' => 'Test',
// ... other fields to fill
));
$this->client->submit($form);
$crawler = $this->client->followRedirect();
// Check data in the show view
$this->assertGreaterThan(0, $crawler->filter('td:contains("Test")')->count(), 'Missing element td:contains("Test")');
// Edit the entity
$crawler = $this->client->click($crawler->selectLink('Edit')->link());
$form = $crawler->selectButton('Update')->form(array(
'artel_profilebundle_services[services]' => 'Foo',
// ... other fields to fill
));
$this->client->submit($form);
$crawler = $this->client->followRedirect();
// Check the element contains an attribute with value equals "Foo"
$this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), 'Missing element [value="Foo"]');
// Delete the entity
$this->client->submit($crawler->selectButton('Delete')->form());
$crawler = $this->client->followRedirect();
// this is line 73
$this->assertNotRegExp('/Foo/', $this->client->getResponse()->getContent());
}
}
in this step I have error
$this->assertNotRegExp('/Foo/', $this->client->getResponse()->getContent());
after deleted test service function assertNotRegExp try to find in content but error something with regular I dint know. After test I have all html my page /admin/services/ and error
' does not match PCRE pattern "/Foo/".
/home/ivan/host/test/src/Artel/AdminBundle/Tests/Controller/ServicesControllerTest.php:73
where error ?
You have to make your request authenticated.
Add the following code to your test class :
private $client = null;
public function setUp()
{
$this->client = static::createClient();
}
private function logIn()
{
$session = $this->client->getContainer()->get('session');
$firewall = 'secured_area';
$token = new UsernamePasswordToken('admin', null, $firewall, array('ROLE_ADMIN'));
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
And use it in your test method before create the client:
public function testCompleteScenario()
{
$this->logIn();
// Do your logic
}
See Simulate authentication in test
Related
I'm creating functionnal tests on a Symfony 3.4 application.
<?php
namespace AppBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\HttpFoundation\Response;
class UserControllerTest extends WebTestCase
{
/**
* Connect to the website while being logged in
* Logs in with (admin, password : a)
*/
public function connection()
{
$client = static::createClient();
$container = static::$kernel->getContainer();
$session = $container->get('session');
// Get the user (has to exist in the database)
$person = self::$kernel->getContainer()->get('doctrine')->getRepository('AppBundle:User')->findOneByUsername('admin');
$token = new UsernamePasswordToken($person, null, 'main', $person->getRoles());
$session->set('_security_main', serialize($token));
$session->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
// Return the client
return $client;
}
public function accessEditPage()
{
$client = $this->connection();
$crawler = $client->request('GET', '/user/');
$this->assertSame(Response::HTTP_OK, $client->getResponse()->getStatusCode());
$this->assertContains(
'Liste des utilisateurices',
$client->getResponse()->getContent()
);
// Select the button of the user created for the test
// Wont work if there are already more than 10 users in the database
$link = $crawler
->filter('tr > td > a:contains("")')
->last()
->link()
;
$crawler = $client->click($link);
return array($client,$crawler);
}
/**
* Create a new user
*/
public function testCreate()
{
$client = $this->connection();
$crawler = $client->request('GET', '/user/new');
$this->assertSame(Response::HTTP_OK, $client->getResponse()->getStatusCode());
// Vérifie si la page affiche le bon texte
$this->assertContains(
'Enregistrer',
$client->getResponse()->getContent()
);
// Select the form and fill its values
$form = $crawler->selectButton(' Créer')->form();
$values = $form->getPhpValues();
$values['appbundle_user']['username'] = 'Jean';
$values['appbundle_user']['plainPassword']['first'] = 'motdepasse';
$values['appbundle_user']['plainPassword']['second'] = 'motdepasse';
$crawler = $client->request($form->getMethod(), $form->getUri(), $values,$form->getPhpFiles());
$crawler = $client->followRedirect();
$this->assertContains(
'Jean',
$client->getResponse()->getContent()
);
}
}
Currently, my Controller tests create databases entries and depends on existing ones and that's a problem.
I want to mock the repositories used in the controller to avoid creating entries when I test my controllers but I haven't found helpful documentation about it. As I can't find documentation, I also wonder if what I want to do is a good practice or not.
I have controller with annotation * #Security("is_granted('PERM_MODULE_OUTBOUND_INVOICES_READ')") and I write test for some action in this controller, create user and loginIn, and when call rout for this action have error
Expression "is_granted('PERM_MODULE_OUTBOUND_INVOICES_READ')" denied access.
when add role to user PERM_MODULE_OUTBOUND_INVOICES_READ still have access denied
when commented tgis and in action check current user is granted have true
/**
* #Route("/manage/new_outbound_invoices", name="new_outbound_invoices")
*/
public function outBoundInvoiceListsAction(Request $request)
{
$check = $this->get('security.authorization_checker')
->isGranted('PERM_MODULE_OUTBOUND_INVOICES_READ', $this->getUser());
but with security annotation access denied why not understand
this is my test
$user = $this->user;
$this->logIn($user);
//$t = $this->getContainer()->get('security.context')->getToken(); try get token and have null, but in action have user from session
$this->client->setServerParameter('HTTP_HOST', 'erp.houseoptima.fi.local');
$crawler = $this->client->request('GET', '/economy/manage/new_outbound_invoices');
this function for LogIn
public function logIn(User $user)
{
$session = $this->client->getContainer()->get('session');
$firewall = 'main';
$token = new UsernamePasswordToken($user, null, $firewall, $user->getRoles());
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
What problem with this security ? With annotation error 403 withot 200 and when check in action is granted user have true
You need to pass the User object
/**
* #Security("is_granted('PERM_MODULE_OUTBOUND_INVOICES_READ', user)")
*/
public function indexAction(User $user)
{
I need to write a functional test in order to test that each role has the correct access to the pages.
In order to do that, I'm simulating authentication with a token, but I slightly edited the logIn method, just to call it with custom $username, $role and $firewall:
protected function logIn($username, $role, $firewall)
{
$session = $this->client->getContainer()->get('session');
$token = new UsernamePasswordToken($username, null, $firewall, $role);
$session->set('_security_' . $firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
So I am able to call it specifying which roles should have the fake user:
$this->logIn('my_username#example.com', ['ROLE_USER'], "my_firewall");
Then I can test if the user is not allowed or not to access certain routes:
// check if the access is correctly denied to the ROLE_USER
$this->client->request('GET', '/route-not-allowed-to-user');
$this->assertEquals(403, $this->client->getResponse()->getStatusCode());
// check if the access is correctly allowed to the ROLE_USER
$this->client->request('GET', '/route-allowed-to-user');
$this->assertNotEquals(403, $this->client->getResponse()->getStatusCode());
Those assertions work, the only problem is that in the view of the route-allowed-to-user I'm using twig to output the username:
{{ app.user.username }}
but it fails. I got status code 500 instead of getting 200, and the following error:
Impossible to access an attribute ("username") on a null variable ...
because app.user is not set.
How can I correctly set the user when simulating an authentication with token?
I think this happens because you didn't go through the authentication process and just created the user token which didn't trigger Symfony's event that store the user's username, roles and so on.
I did a similar thing recently by actually going through login form, filling data and sending it. Just like I was doing a real login attempt and it works well.
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class AuthenticatedTestCase extends KernelTestCase
{
static protected $client;
static public function setUpBeforeClass()
{
parent::setUpBeforeClass();
self::$client = static::$kernel->getContainer()->get('test.client');
}
static public function login($login, $password)
{
$crawler = self::$client->request('GET', '/test_admin/login');
$form = $crawler->filter('input[type="submit"]')->form([
'_username' => $login,
'_password' => $password,
]);
self::$client->submit($form);
// Redirect after successful login
self::assertEquals(302, self::$client->getResponse()->getStatusCode());
self::$client->followRedirect();
if (200 === self::$client->getResponse()->getStatusCode()) {
// Redirected URL is OK
// ...
}
}
}
I've resolved by editing the logIn method as follows:
protected function logIn($username, $password, $firewall)
{
$session = $this->client->getContainer()->get('session');
$authenticationManager = $this->client->getContainer()->get('security.authentication.manager');
$token = $authenticationManager->authenticate(
new UsernamePasswordToken(
$username,
$password,
$firewall
)
);
$session->set('_security_' . $firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
and using doctrine data fixtures in order to set users and roles.
I use Symfony2 with DunglasAngularCsrfBundle and when i run my PHpunit test it get error {"code":403,"message":"Bad CSRF token."}
If auth user with http basic
self::$user_client = static::createClient(array(), array(
'PHP_AUTH_USER' => 'user1#mail.com',
'PHP_AUTH_PW' => 'user1',
'HTTP_HOST' => static::getHost()
));
This condition return false in EventListener\AngularCsrfValidationListener
if (!$value || !$this->angularCsrfTokenManager->isTokenValid($value)) {
throw new AccessDeniedHttpException('Bad CSRF token.');
}
generated token in test not equal, i try another auth like this:
protected function logIn($email)
{
$this->client = static::createClient();
$user = $this->client->getContainer()
->get('doctrine')
->getManager()
->getRepository('UserBundle:User')
->findOneByEmail($email);
$providerKey = static::$kernel->getContainer()->getParameter('fos_user.firewall_name');
$token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
$session = $this->client->getContainer()->get('session');
$session->set('_security_'.$providerKey, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
token not created. Return false in Symfony\Component\Security\Csrf\CsrfTokenManager::isTokenValid:
$this->storage->hasToken($token->getId())
Please help to resolve this issue, thanks
Solved
self::$client->request('GET', '/')
set token in cookie
I am attempting to log a user in programmatically in my functional test on SF 2.7 and FOSUserBundle dev-master. I have already found a good reference to log a user in via SO in this answer - Symfony2 - Tests with FOSUserBundle
The problem is that the second answer, logging the user in programmatically, doesn't work. Here is my code:
<?php
namespace Test\BackEnd\UserBundle\Controller;
use Test\Shared\CoreBundle\Tests\AbstractControllerTest;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\Tools\SchemaTool;
use FA\BackEnd\UserBundle\DataFixtures\ORM\LoadUserData;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class DefaultController extends AbstractControllerTest
{
public function setUp()
{
$this->client = static::createClient();
$container = $this->client->getContainer();
$doctrine = $container->get('doctrine');
$em = $doctrine->getManager();
$schemaTool = new SchemaTool($em);
$metadata = $em->getMetaDataFactory()->getAllMetadata();
// Drop and recreate tables for all entities
$schemaTool->dropSchema($metadata);
$schemaTool->createSchema($metadata);
$loader = new Loader();
$user = new LoadUserData();
$user->setContainer($container);
$loader->addFixture($user);
$purger = new ORMPurger();
$executor = new ORMExecutor($em, $purger);
$executor->execute($loader->getFixtures());
$session = $container->get('session');
$userManager = $container->get('fos_user.user_manager');
$user = $userManager->findUserBy(array('username' => 'test'));
$firewall = 'default';
$token = new UsernamePasswordToken($user, $user->getPassword(), $firewall, $user->getRoles());
self::$kernel->getContainer()->get('security.token_storage')->setToken($token);
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
public function testProfile()
{
//$this->createAuthorizedClient();
$token = $this->client->getContainer()->get('security.token_storage')->getToken();
$this->client->request('GET', '/profile/');
$this->assertEquals(
200,
$this->client->getResponse()->getStatusCode(),
"/profile isn't accessible"
);
}
}
Whenever I set a break point before the route gets executed, the token is return correctly:
Whenever I get to the function getUser() used by the Controller (http://api.symfony.com/2.7/Symfony/Bundle/FrameworkBundle/Controller/Controller.html#method_getUser) PHPStorm returns an empty token as viewed here:
So I decided to try the following code to log a user in, and it works.
$crawler = $this->client->request('GET', '/login');
$form = $crawler->selectButton('_submit')->form(array(
'_username' => 'test',
'_password' => 'test123',
));
$this->client->submit($form);
$this->client->followRedirect();
Am I not doing something properly whenever I log the user in programmatically? Is the session not being set properly?
Thanks!
Rat
I use this:
protected function createAuthorizedClient()
{
$client = static::createClient();
$container = $client->getContainer();
$session = $container->get('session');
$userManager = $container->get('fos_user.user_manager');
$loginManager = $container->get('fos_user.security.login_manager');
$firewallName = $container->getParameter('fos_user.firewall_name');
$user = $userManager->findUserBy(array('username' => 'USERNAME'));
$loginManager->loginUser($firewallName, $user);
// save the login token into the session and put it in a cookie
$container->get('session')->set('_security_' . $firewallName,
serialize($container->get('security.context')->getToken()));
$container->get('session')->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
$this->client = $client;
}
and then in your test:
public function testMiInfo()
{
$this->createAuthorizedClient();
//else..
}