Skip to content

Commit

Permalink
Fix #113: Add support for stringable CSRF tokens
Browse files Browse the repository at this point in the history
Co-authored-by: Sergei Predvoditelev <sergey.predvoditelev@gmail.com>
Co-authored-by: Alexander Makarov <sam@rmcreative.ru>
  • Loading branch information
3 people committed Oct 23, 2021
1 parent cb6799f commit ef46e19
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 14 deletions.
17 changes: 12 additions & 5 deletions src/Widget/Form.php
Expand Up @@ -4,6 +4,7 @@

namespace Yiisoft\Form\Widget;

use InvalidArgumentException;
use Yiisoft\Html\Html;
use Yiisoft\Http\Method;
use Yiisoft\Widget\Widget;
Expand Down Expand Up @@ -156,17 +157,23 @@ public function autocomplete(bool $value = true): self
}

/**
* The csrf-token content attribute token that are known to be safe to use for.
* The CSRF-token content attribute token that are known to be safe to use for.
*
* @param string $csrfToken the csrf-token attribute value.
* @param string $csrfName the csrf-token attribute name.
* @param mixed|string|\Stringable $csrfToken the CSRF-token attribute value.
* @param string $csrfName the CSRF-token attribute name.
*
* @return static
*/
public function csrf(string $csrfToken, string $csrfName = '_csrf'): self
public function csrf($csrfToken, string $csrfName = '_csrf'): self
{
$new = clone $this;
$new->csrfToken = $csrfToken;

if (is_string($csrfToken) || (is_object($csrfToken) && method_exists($csrfToken, '__toString'))) {
$new->csrfToken = (string) $csrfToken;
} else {
throw new InvalidArgumentException('$csrfToken must be a string or \Stringable object.');
}

$new->csrfName = $csrfName;
return $new;
}
Expand Down
42 changes: 33 additions & 9 deletions tests/Widget/FormTest.php
Expand Up @@ -4,12 +4,13 @@

namespace Yiisoft\Form\Tests\Widget;

use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Yiisoft\Form\Tests\Stub\ValidatorMock;
use StdClass;
use Stringable;
use Yiisoft\Form\Tests\TestSupport\TestTrait;
use Yiisoft\Form\Widget\Form;
use Yiisoft\Test\Support\Container\SimpleContainer;
use Yiisoft\Validator\ValidatorInterface;
use Yiisoft\Widget\WidgetFactory;

final class FormTest extends TestCase
Expand Down Expand Up @@ -90,6 +91,19 @@ public function dataProviderCsrf(): array
'tokenCsrf',
'myToken',
],
// object stringable
[
'<form action="/foo" method="POST" myToken="tokenCsrf">' . PHP_EOL .
'<input type="hidden" name="myToken" value="tokenCsrf">',
'POST',
new class () {
public function __toString(): string
{
return 'tokenCsrf';
}
},
'myToken',
],
];
}

Expand All @@ -98,16 +112,31 @@ public function dataProviderCsrf(): array
*
* @param string $expected
* @param string $method
* @param array $attributes
* @param string|Stringable $csrfToken
* @param string $csrfName
*/
public function testCsrf(string $expected, string $method, string $csrfToken, string $csrfName): void
public function testCsrf(string $expected, string $method, $csrfToken, string $csrfName): void
{
$formWidget = $csrfName !== ''
? Form::widget()->action('/foo')->csrf($csrfToken, $csrfName)->method($method)->begin()
: Form::widget()->action('/foo')->csrf($csrfToken)->method($method)->begin();
$this->assertSame($expected, $formWidget);
}

public function testCsrfExceptionNotString(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('$csrfToken must be a string or \Stringable object.');
Form::widget()->action('/foo')->csrf(1)->begin();
}

public function testCsrfExceptionNotStringable(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('$csrfToken must be a string or \Stringable object.');
Form::widget()->action('/foo')->csrf(new StdClass())->begin();
}

public function testEnd(): void
{
Form::widget()->begin();
Expand Down Expand Up @@ -167,9 +196,4 @@ protected function setUp(): void
parent::setUp();
WidgetFactory::initialize(new SimpleContainer(), []);
}

private function createValidatorMock(): ValidatorInterface
{
return new ValidatorMock();
}
}

0 comments on commit ef46e19

Please sign in to comment.