9

What is the best way of creating functional tests to test forms with CSRF protection enabled in Symfony?

Currently I have to add the following code before each form submittion:

  $form = new sfGuardFormSignin();
  $token = $form->getCSRFToken();
  $token_name = $form->getCSRFFieldName();

Then I add the $token and $token_name to form parameters like this:

call('/login', 'POST', array (
    'signin' => 
    array (
      'username' => $username,
      'password' => $password,
      $token_name => $token,
    )))

The option suggested in the documentation:

'_with_csrf' => true,

Doesn't work at all.

Is there more simple way to avoid adding token to each form tested manually? Or is there a way to turn off csrf checking when running tests?

The way I've described above is ok when you have to test 1-2 forms but if project contains tens unique forms it becomes a pain.

Stepashka
  • 2,648
  • 20
  • 23
  • The documentation option uses an instance of BaseForm to extend from; the CSRF token is generated using the type of form though, so MyFooForm != BaseForm and hence that's why the option fails... your option is the way I've ended up doing it too in my projects. – richsage Feb 20 '10 at 16:32
  • There is another reason why it fails as well. It add csrf token to list of parameters but usually in forms it is in arrays (like on example above). So it is completely useless :-( . Thanks for your comment. – Stepashka Feb 21 '10 at 10:23

5 Answers5

4

Of course, you can't use _with_csrf option if you call directly the url. You must pass from the form page, clicking on the submit button. Like so:

click('signin', array('signin' => array('username' => $username, 'password' => $password), array('_with_csrf' => true)))

The string 'signin' must be adapted to your form. You can also use a more label-independent string, like 'form#myform input[type="submit"]' instead of 'signin', adapting the id of your form.

As already suggested, you can disapble CSRF for login, it's really useful for forms that modifies data.

Massimiliano Arione
  • 2,422
  • 19
  • 40
3

I personally don't use functional tests that extensively (probably to my own detriment), but you could always switch the CSRF protection off in your form class for testing purposes.

public function configure ()

  $this->disableLocalCSRFProtection();
Tom
  • 30,090
  • 27
  • 90
  • 124
2

You can disable csrf protection for all forms just by adding additional compiler pass:

class CsrfProtectionCompilerPass implements CompilerPassInterface
{
    /**
     * {@inheritdoc}
     */
    public function process(ContainerBuilder $container)
    {
        $env = $container->getParameter('kernel.environment');
        if ($env == 'test') {
            $container->setParameter('form.type_extension.csrf.enabled', false);
        }
    }
}

Or You can disable form extension completely by adding to config:

framework:
    csrf_protection: false

btw, last solutions works only if You don't have explicitly set form option csrf_protection

0

You should get a CSRF token by showing the page including the form.

$browser->get('/login');
$dom = new DOMDocument('1.0', $browser->getResponse()->getCharset());
$dom->loadHTML($browser->getResponse()->getContent());
$domCssSelector = new sfDomCssSelector($dom);
$token = $domCssSelector->matchSingle('input[name="_csrf_token"]')->getNode()->getAttribute('value');
0

I would turn off CSRF for testing environment.

blockhead
  • 9,655
  • 3
  • 43
  • 69