Codeception acceptance tests and CSRF

Hi,

I have the following in my AcceptanceHelper class:


class AcceptanceHelper extends \Codeception\Module

{

    public function csrf(){

        $csrf_param = Yii::$app->request->csrfParam;

        $csrf_token = Yii::$app->request->csrfToken;

        return [$csrf_param => $csrf_token];

    }

}

I have REST module enabled in my acceptance.suit.yml:


class_name: AcceptanceTester

modules:

    enabled:

         - PhpBrowser

         - AcceptanceHelper

         - REST


    config:

        PhpBrowser:

            url: http://rama

        REST:

            depends: PhpBrowser

And I have the following code in my LoginCept:


$I->amGoingTo('try to log out');

$I->sendPOST('main/logout', $I->csrf());

$I->expectTo('see home page');

$I->seeLink('Log in');

Infortunately, for whatever reason my test always fails with "Bad Request (#400): Unable to verify your data submission".

Disabling CSRF in controller with:


public $enableCsrfValidation = false;

fixes the issue but this is not any solution.

Please, how to overcome the problem with CSRF validation within Codeception acceptance tests!

Any idea?

Well,

With the lack of better solution - this one is working with Codeception 2.1.*:

This is AcceptanceHelper:


<?php

namespace Codeception\Module;


use Yii;


// here you can define custom actions

// all public methods declared in helper class will be available in $I


class AcceptanceHelper extends \Codeception\Module

{

    public function csrf(){

        $file = codecept_output_dir() . 'page.html';

        $this->getModule('PhpBrowser')->_savePageSource($file);

        $page = file_get_contents($file);

        $csrf_param = Yii::$app->request->csrfParam;

        if(preg_match('/<meta name="csrf-token" content="(.*?)">/', $page, $match)){

            $csrf_token = $match[1];

        }else{

            fwrite(STDOUT, "\r\n" . 'ERROR: COULD NOT FIND CSRF TOKEN');

            //this token will not work!!!

            $csrf_token = Yii::$app->request->csrfToken;

        }

        unlink($file);

        return [$csrf_param => $csrf_token];

    }

}

And this is how to use this to test logout action in case it is CSRF protected and limited to POST verb:


$I->sendPOST('main/logout', $I->csrf());

Not perfect but acceptable. I am open for any suggestions.

For functional tests I solved probles like this

class FunctionalTester extends \Codeception\Actor
public function getCsrf()
{
// XPath expression - doesnt work due html validation issues
//string(//meta[@name=‘csrf-param’]/@content)// param name
//string(//meta[@name=‘csrf-token’]/@content)// token.
$source = $this->grabPageSource();
if( preg_match(’//’, $source, $matches) > 0 )
{
$param = $matches[0];
$param = substr( $param, strpos($param, ‘content="’)+9 );
$param = substr( $param, 0, strpos($param, ‘"’) );

          if( preg_match('/<meta name="csrf-token" content="(\S)+?">/', $source, $matches) > 0 )
          {
            $token = $matches[0];
            $token = substr( $token, strpos($token, 'content="')+9 );
            $token = substr( $token, 0, strpos($token, '"') );
            return [ $param => $token ];
          }
        }
        return false;
    }

Used as

$csrf = $I->getCsrf();