summaryrefslogtreecommitdiffstats
path: root/Csrf/Tests/TokenStorage
diff options
context:
space:
mode:
authorFabien Potencier <fabien.potencier@gmail.com>2013-09-30 17:35:08 +0200
committerFabien Potencier <fabien.potencier@gmail.com>2013-09-30 17:35:08 +0200
commitd1ad5baedd91d60afac0c4ae1cd4b12fea20dc30 (patch)
tree79227616b22e4d38c8c9d48dc2ee91d957301abd /Csrf/Tests/TokenStorage
parent46c7d3e11f3ab534ce84dfaeadd7c2870dba1a36 (diff)
parent78f7ee0a8a60284b74c14dbbe601de26ded1350e (diff)
downloadsymfony-security-d1ad5baedd91d60afac0c4ae1cd4b12fea20dc30.zip
symfony-security-d1ad5baedd91d60afac0c4ae1cd4b12fea20dc30.tar.gz
symfony-security-d1ad5baedd91d60afac0c4ae1cd4b12fea20dc30.tar.bz2
feature#6554 [Security] Added Security\Csrf sub-component with better token generation (bschussek)
This PR was merged into the master branch. Discussion ---------- [Security] Added Security\Csrf sub-component with better token generation | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | TODO **Update September 27, 2013** This PR simplifies the CSRF mechanism to generate completely random tokens. A random token is generated once per ~~intention~~ token ID and then stored in the session. Tokens are valid until the session expires. Since the CSRF token generator depends on `StringUtils` and `SecureRandom` from Security\Core, and since Security\Http currently depends on the Form component for token generation, I decided to add a new Security\Csrf sub-component that contains the improved CSRF token generator. Consequences: * Security\Http now depends on Security\Csrf instead of Form * Form now optionally depends on Security\Csrf * The configuration for the "security.secure_random" service and the "security.csrf.*" services was moved to FrameworkBundle to guarantee BC In the new Security\Csrf sub-component, I tried to improve the naming where I could do so without breaking BC: * CSRF "providers" are now called "token generators" * CSRF "intentions" are now called "token IDs", because that's really what they are ##### TODO - [ ] The documentation needs to be checked for references to the configuration of the application secret. Remarks that the secret is used for CSRF protection need to be removed. - [ ] Add aliases "csrf_token_generator" and "csrf_token_id" for "csrf_provider" and "intention" in the SecurityBundle configuration - [x] Make sure `SecureRandom` never blocks for `CsrfTokenGenerator` Commits ------- 7f02304 [Security] Added missing PHPDoc tag 2e04e32 Updated Composer dependencies to require the Security\Csrf component where necessary bf85e83 [FrameworkBundle][SecurityBundle] Added service configuration for the new Security CSRF sub-component 2048cf6 [Form] Deprecated the CSRF implementation and added an optional dependency to the Security CSRF sub-component instead 85d4959 [Security] Changed Security HTTP sub-component to depend on CSRF sub-component instead of Form 1bf1640 [Security] Added CSRF sub-component
Diffstat (limited to 'Csrf/Tests/TokenStorage')
-rw-r--r--Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php99
-rw-r--r--Csrf/Tests/TokenStorage/SessionTokenStorageTest.php144
2 files changed, 243 insertions, 0 deletions
diff --git a/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php b/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php
new file mode 100644
index 0000000..69df061
--- /dev/null
+++ b/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php
@@ -0,0 +1,99 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Csrf\CsrfProvider;
+
+use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @runTestsInSeparateProcesses
+ */
+class NativeSessionTokenStorageTest extends \PHPUnit_Framework_TestCase
+{
+ const SESSION_NAMESPACE = 'foobar';
+
+ /**
+ * @var NativeSessionTokenStorage
+ */
+ private $storage;
+
+ public static function setUpBeforeClass()
+ {
+ ini_set('session.save_handler', 'files');
+ ini_set('session.save_path', sys_get_temp_dir());
+
+ parent::setUpBeforeClass();
+ }
+
+ protected function setUp()
+ {
+ $_SESSION = array();
+
+ $this->storage = new NativeSessionTokenStorage(self::SESSION_NAMESPACE);
+ }
+
+ public function testStoreTokenInClosedSession()
+ {
+ $this->storage->setToken('token_id', 'TOKEN');
+
+ $this->assertSame(array(self::SESSION_NAMESPACE => array('token_id' => 'TOKEN')), $_SESSION);
+ }
+
+ public function testStoreTokenInClosedSessionWithExistingSessionId()
+ {
+ session_id('foobar');
+
+ $this->assertSame(PHP_SESSION_NONE, session_status());
+
+ $this->storage->setToken('token_id', 'TOKEN');
+
+ $this->assertSame(PHP_SESSION_ACTIVE, session_status());
+ $this->assertSame(array(self::SESSION_NAMESPACE => array('token_id' => 'TOKEN')), $_SESSION);
+ }
+
+ public function testStoreTokenInActiveSession()
+ {
+ session_start();
+
+ $this->storage->setToken('token_id', 'TOKEN');
+
+ $this->assertSame(array(self::SESSION_NAMESPACE => array('token_id' => 'TOKEN')), $_SESSION);
+ }
+
+ /**
+ * @depends testStoreTokenInClosedSession
+ */
+ public function testCheckToken()
+ {
+ $this->assertFalse($this->storage->hasToken('token_id'));
+
+ $this->storage->setToken('token_id', 'TOKEN');
+
+ $this->assertTrue($this->storage->hasToken('token_id'));
+ }
+
+ /**
+ * @depends testStoreTokenInClosedSession
+ */
+ public function testGetExistingToken()
+ {
+ $this->storage->setToken('token_id', 'TOKEN');
+
+ $this->assertSame('TOKEN', $this->storage->getToken('token_id'));
+ }
+
+ public function testGetNonExistingToken()
+ {
+ $this->assertSame('DEFAULT', $this->storage->getToken('token_id', 'DEFAULT'));
+ }
+}
diff --git a/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php b/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php
new file mode 100644
index 0000000..5c8c173
--- /dev/null
+++ b/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php
@@ -0,0 +1,144 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Csrf\CsrfProvider;
+
+use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class SessionTokenStorageTest extends \PHPUnit_Framework_TestCase
+{
+ const SESSION_NAMESPACE = 'foobar';
+
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $session;
+
+ /**
+ * @var SessionTokenStorage
+ */
+ private $storage;
+
+ protected function setUp()
+ {
+ if (!class_exists('Symfony\Component\HttpFoundation\Session\SessionInterface')) {
+ $this->markTestSkipped('The "HttpFoundation" component is not available');
+ }
+
+ $this->session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->storage = new SessionTokenStorage($this->session, self::SESSION_NAMESPACE);
+ }
+
+ public function testStoreTokenInClosedSession()
+ {
+ $this->session->expects($this->any())
+ ->method('isStarted')
+ ->will($this->returnValue(false));
+
+ $this->session->expects($this->once())
+ ->method('start');
+
+ $this->session->expects($this->once())
+ ->method('set')
+ ->with(self::SESSION_NAMESPACE.'/token_id', 'TOKEN');
+
+ $this->storage->setToken('token_id', 'TOKEN');
+ }
+
+ public function testStoreTokenInActiveSession()
+ {
+ $this->session->expects($this->any())
+ ->method('isStarted')
+ ->will($this->returnValue(true));
+
+ $this->session->expects($this->never())
+ ->method('start');
+
+ $this->session->expects($this->once())
+ ->method('set')
+ ->with(self::SESSION_NAMESPACE.'/token_id', 'TOKEN');
+
+ $this->storage->setToken('token_id', 'TOKEN');
+ }
+
+ public function testCheckTokenInClosedSession()
+ {
+ $this->session->expects($this->any())
+ ->method('isStarted')
+ ->will($this->returnValue(false));
+
+ $this->session->expects($this->once())
+ ->method('start');
+
+ $this->session->expects($this->once())
+ ->method('has')
+ ->with(self::SESSION_NAMESPACE.'/token_id')
+ ->will($this->returnValue('RESULT'));
+
+ $this->assertSame('RESULT', $this->storage->hasToken('token_id'));
+ }
+
+ public function testCheckTokenInActiveSession()
+ {
+ $this->session->expects($this->any())
+ ->method('isStarted')
+ ->will($this->returnValue(true));
+
+ $this->session->expects($this->never())
+ ->method('start');
+
+ $this->session->expects($this->once())
+ ->method('has')
+ ->with(self::SESSION_NAMESPACE.'/token_id')
+ ->will($this->returnValue('RESULT'));
+
+ $this->assertSame('RESULT', $this->storage->hasToken('token_id'));
+ }
+
+ public function testGetTokenFromClosedSession()
+ {
+ $this->session->expects($this->any())
+ ->method('isStarted')
+ ->will($this->returnValue(false));
+
+ $this->session->expects($this->once())
+ ->method('start');
+
+ $this->session->expects($this->once())
+ ->method('get')
+ ->with(self::SESSION_NAMESPACE.'/token_id', 'DEFAULT')
+ ->will($this->returnValue('RESULT'));
+
+ $this->assertSame('RESULT', $this->storage->getToken('token_id', 'DEFAULT'));
+ }
+
+ public function testGetTokenFromActiveSession()
+ {
+ $this->session->expects($this->any())
+ ->method('isStarted')
+ ->will($this->returnValue(true));
+
+ $this->session->expects($this->never())
+ ->method('start');
+
+ $this->session->expects($this->once())
+ ->method('get')
+ ->with(self::SESSION_NAMESPACE.'/token_id', 'DEFAULT')
+ ->will($this->returnValue('RESULT'));
+
+ $this->assertSame('RESULT', $this->storage->getToken('token_id', 'DEFAULT'));
+ }
+}