How to implement CSRF tokens ? #4

Open
dadinugroho opened this Issue Apr 5, 2014 · 4 comments

Comments

Projects
None yet
2 participants

When we enable csrf, the error will appear "The CSRF token could not be verified". Where we can add the parameter for csrf?

My current workaround is to introduce new variable called csrfToken.

my admin.php where the GridView is defined:

array('name' => 'barcode',
            'class' => 'EEditableColumn', 'editable_type' => 'editbox', 'csrfToken' => Yii::app()->request->csrfToken,
            'action' => array('/product/ajaxEditColumn'),
        ),

In EEditableColumn.php

...
    public $csrfToken;
    public $editable_type;  // "editbox","select" (required array in editable_options)
    public $editable_options; // array(1=>'yes',0=>'no')
    public $action;    // the action receptor when receiving changes. 

    //  array('someaction')

    public function renderDataCell($row) {
        $dummy = new EEditable($this->grid->id);
        $data = $this->grid->dataProvider->data[$row];
        $options = $this->htmlOptions;
        if ($this->cssClassExpression !== null) {
            $class = $this->evaluateExpression($this->cssClassExpression, array('row' => $row, 'data' => $data));
            if (!empty($class)) {
                if (isset($options['class']))
                    $options['class'].=' ' . $class;
                else
                    $options['class'] = $class;
            }
        }
        if (null != $this->editable_type) {
            $options['editable_type'] = $this->editable_type;
            $options['editable_name'] = $this->name;
            $keyValue = "0";
            if (isset($this->grid->dataProvider->keyField)) {
                $dpKeyField = $this->grid->dataProvider->keyField;
                if (!isset($data[$dpKeyField]))
                    throw new Exception("The provided keyField '$dpKeyField' "
                    . "is not defined in your data columns or array indexes");
                $keyValue = $data[$dpKeyField];
            }else {
                $keyValue = $data->primarykey;
            }
            $options['editable_action'] = CHtml::normalizeUrl($this->action);
            $options['editable_id'] = $keyValue;
            $options['editable_csrf'] = $this->csrfToken;
        }
        echo CHtml::openTag('td', $options);
...

In eeditable.js,

...
 if ((new_value != null) && (new_value != saved_value)) {
            tag.find('input').attr('disabled', 'disabled');
            $.ajax({
                url: tag.attr('editable_action'), type: 'post',
                data: {"YII_CSRF_TOKEN": tag.attr('editable_csrf'), keyvalue: _getKeyValue(tag),
                    name: _getKeyName(tag), old_value: saved_value,
                    new_value: new_value},
                success: function(response) {
                    tag.data('eeditablegrid_value', response);
                    _reset(tag);
                    tag.find('input').attr('disabled', null);
                },
...

That's what I have been done to make it work. But, I hope you can find better and more simple solution.

Cheers,

Daniel

Owner

christiansalazar commented Apr 5, 2014

thank u very much. reading your post carefully.

Owner

christiansalazar commented Apr 5, 2014

Your solution is nicely implemented. Looking ahead in the base-base class: http://www.yiiframework.com/doc/api/1.1/CGridColumn#htmlOptions-detail you'll find the htmlOptions attribute, using that attribute you can skeep the code change at EEditableColumn by simply providing the token in:

[php]
array('name' => 'barcode',
            'class' => 'EEditableColumn', 'editable_type' => 'editbox', 

            // instead of:
            // 'csrfToken' => Yii::app()->request->csrfToken, 
           // do it in this way:
           'htmlOptions'=>array('csrfToken' => Yii::app()->request->csrfToken,),

            'action' => array('/product/ajaxEditColumn'),
        ),

the next change required is that made by you in the assets/eeditable.js:

[javascript]
...
 if ((new_value != null) && (new_value != saved_value)) {
            tag.find('input').attr('disabled', 'disabled');
            $.ajax({
                url: tag.attr('editable_action'), type: 'post',
                data: {"YII_CSRF_TOKEN": tag.attr('editable_csrf'), keyvalue: _getKeyValue(tag),
                    name: _getKeyName(tag), old_value: saved_value,
                    new_value: new_value},
                success: function(response) {
                    tag.data('eeditablegrid_value', response);
                    _reset(tag);
                    tag.find('input').attr('disabled', null);
                },
...

maybe later i can implement options available for this ajax call,

christiansalazar added a commit that referenced this issue Apr 5, 2014

@christiansalazar christiansalazar changed the title from Csrf error to How to implement CSRF tokens ? Apr 5, 2014

Googling on this topic, there is a suggestion to add a CActiveForm to enclose the input and in ajax call we only need to serialize the form. Hence, if we enable csrf, the CActiveForm will add the csrf token into a hidden field. What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment