Skip to content
Browse files

bug #9216 [Security\Csrf] Split CsrfTokenGenerator into CsrfTokenMana…

…ger and TokenGenerator (bschussek)

This PR was merged into the master branch.

Discussion
----------

[Security\Csrf] Split CsrfTokenGenerator into CsrfTokenManager and TokenGenerator

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #9210
| License       | MIT
| Doc PR        | -

This is a follow-up PR of #6554 that splits the CsrfTokenGenerator into two separate classes for generating and managing CSRF tokens. As a consequence, it is now possible to explicitly remove or refresh CSRF tokens if they should be used only once. See #9210 for more information.

Commits
-------

d4bb5f4 [Security\Csrf] Split CsrfTokenGenerator into CsrfTokenManager and TokenGenerator
  • Loading branch information...
2 parents 4f19105 + d4bb5f4 commit 911b328dc197531be7301f8bffa8f63232b6b58d @fabpot fabpot committed Oct 7, 2013
Showing with 1,251 additions and 459 deletions.
  1. +1 −1 UPGRADE-3.0.md
  2. +12 −3 src/Symfony/Bridge/Twig/Form/TwigRenderer.php
  3. +3 −1 src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
  4. +8 −3 src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml
  5. +1 −1 src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php
  6. +1 −1 src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php
  7. +18 −10 src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php
  8. +2 −2 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml
  9. +18 −9 src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php
  10. +75 −0 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderAdapter.php
  11. +34 −5 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.php
  12. +0 −26 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfTokenGeneratorAdapter.php
  13. +57 −0 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfTokenManagerAdapter.php
  14. +1 −1 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php
  15. +1 −1 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php
  16. +16 −6 src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
  17. +28 −16 src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php
  18. +12 −3 src/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php
  19. +14 −8 src/Symfony/Component/Form/FormRenderer.php
  20. +4 −3 src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php
  21. +4 −4 src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
  22. +4 −3 src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php
  23. +4 −4 src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php
  24. +33 −32 src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php
  25. +21 −0 src/Symfony/Component/Security/Core/Exception/ExceptionInterface.php
  26. +21 −0 src/Symfony/Component/Security/Core/Exception/InvalidArgumentException.php
  27. +21 −0 src/Symfony/Component/Security/Core/Exception/RuntimeException.php
  28. +66 −0 src/Symfony/Component/Security/Csrf/CsrfToken.php
  29. +0 −105 src/Symfony/Component/Security/Csrf/CsrfTokenGenerator.php
  30. +106 −0 src/Symfony/Component/Security/Csrf/CsrfTokenManager.php
  31. +67 −0 src/Symfony/Component/Security/Csrf/CsrfTokenManagerInterface.php
  32. +21 −0 src/Symfony/Component/Security/Csrf/Exception/TokenNotFoundException.php
  33. +1 −1 src/Symfony/Component/Security/Csrf/README.md
  34. +0 −148 src/Symfony/Component/Security/Csrf/Tests/CsrfTokenGeneratorTest.php
  35. +164 −0 src/Symfony/Component/Security/Csrf/Tests/CsrfTokenManagerTest.php
  36. +71 −0 src/Symfony/Component/Security/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php
  37. +24 −1 src/Symfony/Component/Security/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php
  38. +124 −6 src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php
  39. +4 −16 ...nent/Security/Csrf/{CsrfTokenGeneratorInterface.php → TokenGenerator/TokenGeneratorInterface.php}
  40. +73 −0 src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php
  41. +25 −5 src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php
  42. +21 −4 src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php
  43. +16 −6 src/Symfony/Component/Security/Csrf/TokenStorage/TokenStorageInterface.php
  44. +21 −11 src/Symfony/Component/Security/Http/Firewall/LogoutListener.php
  45. +17 −7 src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php
  46. +16 −6 src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
View
2 UPGRADE-3.0.md
@@ -170,7 +170,7 @@ UPGRADE FROM 2.x to 3.0
* The interface `Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface`
and all of its implementations were removed. Use the new interface
- `Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface` instead.
+ `Symfony\Component\Security\Csrf\CsrfTokenManagerInterface` instead.
* The options "csrf_provider" and "intention" were renamed to "csrf_token_generator"
and "csrf_token_id".
View
15 src/Symfony/Bridge/Twig/Form/TwigRenderer.php
@@ -11,8 +11,11 @@
namespace Symfony\Bridge\Twig\Form;
+use Symfony\Component\Form\Exception\UnexpectedTypeException;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\Form\FormRenderer;
-use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
@@ -24,9 +27,15 @@ class TwigRenderer extends FormRenderer implements TwigRendererInterface
*/
private $engine;
- public function __construct(TwigRendererEngineInterface $engine, CsrfTokenGeneratorInterface $csrfTokenGenerator = null)
+ public function __construct(TwigRendererEngineInterface $engine, $csrfTokenManager = null)
{
- parent::__construct($engine, $csrfTokenGenerator);
+ if ($csrfTokenManager instanceof CsrfProviderInterface) {
+ $csrfTokenManager = new CsrfProviderAdapter($csrfTokenManager);
+ } elseif (null !== $csrfTokenManager && !$csrfTokenManager instanceof CsrfTokenManagerInterface) {
+ throw new UnexpectedTypeException($csrfTokenManager, 'CsrfProviderInterface or CsrfTokenManagerInterface');
+ }
+
+ parent::__construct($engine, $csrfTokenManager);
$this->engine = $engine;
}
View
4 src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml
@@ -5,7 +5,9 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
- <service id="form.csrf_provider" class="Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfTokenGeneratorAdapter" parent="security.csrf.token_generator" />
+ <service id="form.csrf_provider" class="Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfTokenManagerAdapter">
+ <argument type="service" id="security.csrf.token_manager" />
+ </service>
<service id="form.type_extension.csrf" class="Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension">
<tag name="form.type_extension" alias="form" />
View
11 src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml
@@ -5,18 +5,23 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
- <parameter key="security.csrf.token_generator.class">Symfony\Component\Security\Csrf\CsrfTokenGenerator</parameter>
+ <parameter key="security.csrf.token_generator.class">Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator</parameter>
<parameter key="security.csrf.token_storage.class">Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage</parameter>
+ <parameter key="security.csrf.token_manager.class">Symfony\Component\Security\Csrf\CsrfTokenManager</parameter>
</parameters>
<services>
+ <service id="security.csrf.token_generator" class="%security.csrf.token_generator.class%" public="false">
+ <argument type="service" id="security.secure_random" />
+ </service>
+
<service id="security.csrf.token_storage" class="%security.csrf.token_storage.class%" public="false">
<argument type="service" id="session" />
</service>
- <service id="security.csrf.token_generator" class="%security.csrf.token_generator.class%">
+ <service id="security.csrf.token_manager" class="%security.csrf.token_manager.class%">
+ <argument type="service" id="security.csrf.token_generator" />
<argument type="service" id="security.csrf.token_storage" />
- <argument type="service" id="security.secure_random" />
</service>
</services>
</container>
View
2 src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php
@@ -46,7 +46,7 @@ protected function getExtensions()
));
return array_merge(parent::getExtensions(), array(
- new TemplatingExtension($this->engine, $this->csrfTokenGenerator, array(
+ new TemplatingExtension($this->engine, $this->csrfTokenManager, array(
'FrameworkBundle:Form',
)),
));
View
2 src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php
@@ -46,7 +46,7 @@ protected function getExtensions()
));
return array_merge(parent::getExtensions(), array(
- new TemplatingExtension($this->engine, $this->csrfTokenGenerator, array(
+ new TemplatingExtension($this->engine, $this->csrfTokenManager, array(
'FrameworkBundle:Form',
'FrameworkBundle:FormTable',
)),
View
28 src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php
@@ -12,8 +12,10 @@
namespace Symfony\Bundle\SecurityBundle\Templating\Helper;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
-use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Templating\Helper\Helper;
/**
@@ -43,15 +45,21 @@ public function __construct(ContainerInterface $container, UrlGeneratorInterface
/**
* Registers a firewall's LogoutListener, allowing its URL to be generated.
*
- * @param string $key The firewall key
- * @param string $logoutPath The path that starts the logout process
- * @param string $csrfTokenId The ID of the CSRF token
- * @param string $csrfParameter The CSRF token parameter name
- * @param CsrfTokenGeneratorInterface $csrfTokenGenerator A CsrfTokenGeneratorInterface instance
+ * @param string $key The firewall key
+ * @param string $logoutPath The path that starts the logout process
+ * @param string $csrfTokenId The ID of the CSRF token
+ * @param string $csrfParameter The CSRF token parameter name
+ * @param CsrfTokenManagerInterface $csrfTokenManager A CsrfTokenManagerInterface instance
*/
- public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, CsrfTokenGeneratorInterface $csrfTokenGenerator = null)
+ public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager = null)
{
- $this->listeners[$key] = array($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenGenerator);
+ if ($csrfTokenManager instanceof CsrfProviderInterface) {
+ $csrfTokenManager = new CsrfProviderAdapter($csrfTokenManager);
+ } elseif (null !== $csrfTokenManager && !$csrfTokenManager instanceof CsrfTokenManagerInterface) {
+ throw new \InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.');
+ }
+
+ $this->listeners[$key] = array($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager);
}
/**
@@ -94,9 +102,9 @@ private function generateLogoutUrl($key, $referenceType)
throw new \InvalidArgumentException(sprintf('No LogoutListener found for firewall key "%s".', $key));
}
- list($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenGenerator) = $this->listeners[$key];
+ list($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager) = $this->listeners[$key];
- $parameters = null !== $csrfTokenGenerator ? array($csrfParameter => $csrfTokenGenerator->generateCsrfToken($csrfTokenId)) : array();
+ $parameters = null !== $csrfTokenManager ? array($csrfParameter => (string) $csrfTokenManager->getToken($csrfTokenId)) : array();
if ('/' === $logoutPath[0]) {
$request = $this->container->get('request');
View
4 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml
@@ -37,12 +37,12 @@ security:
username_parameter: "user_login[username]"
password_parameter: "user_login[password]"
csrf_parameter: "user_login[_token]"
- csrf_provider: security.csrf.token_generator
+ csrf_provider: security.csrf.token_manager
anonymous: ~
logout:
path: /logout_path
target: /
- csrf_provider: security.csrf.token_generator
+ csrf_provider: security.csrf.token_manager
access_control:
- { path: .*, roles: IS_AUTHENTICATED_FULLY }
View
27 src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php
@@ -11,9 +11,12 @@
namespace Symfony\Component\Form\Extension\Csrf;
+use Symfony\Component\Form\Exception\UnexpectedTypeException;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\Form\Extension\Csrf\Type;
use Symfony\Component\Form\AbstractExtension;
-use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Translation\TranslatorInterface;
/**
@@ -24,9 +27,9 @@
class CsrfExtension extends AbstractExtension
{
/**
- * @var CsrfTokenGeneratorInterface
+ * @var CsrfTokenManagerInterface
*/
- private $tokenGenerator;
+ private $tokenManager;
/**
* @var TranslatorInterface
@@ -41,13 +44,19 @@ class CsrfExtension extends AbstractExtension
/**
* Constructor.
*
- * @param CsrfTokenGeneratorInterface $tokenGenerator The CSRF token generator
- * @param TranslatorInterface $translator The translator for translating error messages
- * @param null|string $translationDomain The translation domain for translating
+ * @param CsrfTokenManagerInterface $tokenManager The CSRF token manager
+ * @param TranslatorInterface $translator The translator for translating error messages
+ * @param null|string $translationDomain The translation domain for translating
*/
- public function __construct(CsrfTokenGeneratorInterface $tokenGenerator, TranslatorInterface $translator = null, $translationDomain = null)
+ public function __construct($tokenManager, TranslatorInterface $translator = null, $translationDomain = null)
{
- $this->tokenGenerator = $tokenGenerator;
+ if ($tokenManager instanceof CsrfProviderInterface) {
+ $tokenManager = new CsrfProviderAdapter($tokenManager);
+ } elseif (!$tokenManager instanceof CsrfTokenManagerInterface) {
+ throw new UnexpectedTypeException($tokenManager, 'CsrfProviderInterface or CsrfTokenManagerInterface');
+ }
+
+ $this->tokenManager = $tokenManager;
$this->translator = $translator;
$this->translationDomain = $translationDomain;
}
@@ -58,7 +67,7 @@ public function __construct(CsrfTokenGeneratorInterface $tokenGenerator, Transla
protected function loadTypeExtensions()
{
return array(
- new Type\FormTypeCsrfExtension($this->tokenGenerator, true, '_token', $this->translator, $this->translationDomain),
+ new Type\FormTypeCsrfExtension($this->tokenManager, true, '_token', $this->translator, $this->translationDomain),
);
}
}
View
75 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderAdapter.php
@@ -0,0 +1,75 @@
+<?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\Extension\Csrf\CsrfProvider;
+
+use Symfony\Component\Form\Exception\BadMethodCallException;
+use Symfony\Component\Security\Csrf\CsrfToken;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
+
+/**
+ * Adapter for using old CSRF providers where the new {@link CsrfTokenManagerInterface}
+ * is expected.
+ *
+ * @since 2.4
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @deprecated Deprecated since version 2.4, to be removed in Symfony 3.0.
+ */
+class CsrfProviderAdapter implements CsrfTokenManagerInterface
+{
+ /**
+ * @var CsrfProviderInterface
+ */
+ private $csrfProvider;
+
+ public function __construct(CsrfProviderInterface $csrfProvider)
+ {
+ $this->csrfProvider = $csrfProvider;
+ }
+
+ public function getCsrfProvider()
+ {
+ return $this->csrfProvider;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getToken($tokenId)
+ {
+ return $this->csrfProvider->generateCsrfToken($tokenId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function refreshToken($tokenId)
+ {
+ throw new BadMethodCallException('Not supported');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeToken($tokenId)
+ {
+ throw new BadMethodCallException('Not supported');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isTokenValid(CsrfToken $token)
+ {
+ return $this->csrfProvider->isCsrfTokenValid($token->getId(), $token->getValue());
+ }
+}
View
39 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.php
@@ -11,16 +11,45 @@
namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider;
-use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
-
/**
- * Alias interface of {@link CsrfTokenGeneratorInterface}.
+ * Marks classes able to provide CSRF protection
+ *
+ * You can generate a CSRF token by using the method generateCsrfToken(). To
+ * this method you should pass a value that is unique to the page that should
+ * be secured against CSRF attacks. This value doesn't necessarily have to be
+ * secret. Implementations of this interface are responsible for adding more
+ * secret information.
+ *
+ * If you want to secure a form submission against CSRF attacks, you could
+ * supply an "intention" string. This way you make sure that the form can only
+ * be submitted to pages that are designed to handle the form, that is, that use
+ * the same intention string to validate the CSRF token with isCsrfTokenValid().
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated Deprecated since version 2.4, to be removed in Symfony 3.0. Use
- * {@link CsrfTokenGeneratorInterface} instead.
+ * {@link \Symfony\Component\Security\Csrf\CsrfTokenManagerInterface}
+ * instead.
*/
-interface CsrfProviderInterface extends CsrfTokenGeneratorInterface
+interface CsrfProviderInterface
{
+ /**
+ * Generates a CSRF token for a page of your application.
+ *
+ * @param string $intention Some value that identifies the action intention
+ * (i.e. "authenticate"). Doesn't have to be a secret value.
+ *
+ * @return string The generated token
+ */
+ public function generateCsrfToken($intention);
+
+ /**
+ * Validates a CSRF token.
+ *
+ * @param string $intention The intention used when generating the CSRF token
+ * @param string $token The token supplied by the browser
+ *
+ * @return Boolean Whether the token supplied by the browser is correct
+ */
+ public function isCsrfTokenValid($intention, $token);
}
View
26 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfTokenGeneratorAdapter.php
@@ -1,26 +0,0 @@
-<?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\Extension\Csrf\CsrfProvider;
-
-use Symfony\Component\Security\Csrf\CsrfTokenGenerator;
-
-/**
- * Adapter for using the new token generator with the old interface.
- *
- * @since 2.4
- * @author Bernhard Schussek <bschussek@gmail.com>
- *
- * @deprecated Deprecated since version 2.4, to be removed in Symfony 3.0.
- */
-class CsrfTokenGeneratorAdapter extends CsrfTokenGenerator implements CsrfProviderInterface
-{
-}
View
57 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfTokenManagerAdapter.php
@@ -0,0 +1,57 @@
+<?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\Extension\Csrf\CsrfProvider;
+
+use Symfony\Component\Security\Csrf\CsrfToken;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
+
+/**
+ * Adapter for using the new token generator with the old interface.
+ *
+ * @since 2.4
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ *
+ * @deprecated Deprecated since version 2.4, to be removed in Symfony 3.0.
+ */
+class CsrfTokenManagerAdapter implements CsrfProviderInterface
+{
+ /**
+ * @var CsrfTokenManagerInterface
+ */
+ private $tokenManager;
+
+ public function __construct(CsrfTokenManagerInterface $tokenManager)
+ {
+ $this->tokenManager = $tokenManager;
+ }
+
+ public function getTokenManager()
+ {
+ return $this->tokenManager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function generateCsrfToken($intention)
+ {
+ return $this->tokenManager->getToken($intention)->getValue();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isCsrfTokenValid($intention, $token)
+ {
+ return $this->tokenManager->isTokenValid(new CsrfToken($intention, $token));
+ }
+}
View
2 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php
@@ -20,7 +20,7 @@
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated Deprecated since version 2.4, to be removed in Symfony 3.0. Use
- * {@link \Symfony\Component\Security\Csrf\CsrfTokenGenerator} in
+ * {@link \Symfony\Component\Security\Csrf\CsrfTokenManager} in
* combination with {@link \Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage}
* instead.
*/
View
2 src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php
@@ -22,7 +22,7 @@
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated Deprecated since version 2.4, to be removed in Symfony 3.0. Use
- * {@link \Symfony\Component\Security\Csrf\CsrfTokenGenerator} in
+ * {@link \Symfony\Component\Security\Csrf\CsrfTokenManager} in
* combination with {@link \Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage}
* instead.
*/
View
22 src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
@@ -12,10 +12,14 @@
namespace Symfony\Component\Form\Extension\Csrf\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Form\Exception\UnexpectedTypeException;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
-use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
+use Symfony\Component\Security\Csrf\CsrfToken;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Translation\TranslatorInterface;
/**
@@ -31,9 +35,9 @@ class CsrfValidationListener implements EventSubscriberInterface
/**
* The generator for CSRF tokens
- * @var CsrfTokenGeneratorInterface
+ * @var CsrfTokenManagerInterface
*/
- private $tokenGenerator;
+ private $tokenManager;
/**
* A text mentioning the tokenId of the CSRF token
@@ -68,10 +72,16 @@ public static function getSubscribedEvents()
);
}
- public function __construct($fieldName, CsrfTokenGeneratorInterface $tokenGenerator, $tokenId, $errorMessage, TranslatorInterface $translator = null, $translationDomain = null)
+ public function __construct($fieldName, $tokenManager, $tokenId, $errorMessage, TranslatorInterface $translator = null, $translationDomain = null)
{
+ if ($tokenManager instanceof CsrfProviderInterface) {
+ $tokenManager = new CsrfProviderAdapter($tokenManager);
+ } elseif (!$tokenManager instanceof CsrfTokenManagerInterface) {
+ throw new UnexpectedTypeException($tokenManager, 'CsrfProviderInterface or CsrfTokenManagerInterface');
+ }
+
$this->fieldName = $fieldName;
- $this->tokenGenerator = $tokenGenerator;
+ $this->tokenManager = $tokenManager;
$this->tokenId = $tokenId;
$this->errorMessage = $errorMessage;
$this->translator = $translator;
@@ -84,7 +94,7 @@ public function preSubmit(FormEvent $event)
$data = $event->getData();
if ($form->isRoot() && $form->getConfig()->getOption('compound')) {
- if (!isset($data[$this->fieldName]) || !$this->tokenGenerator->isCsrfTokenValid($this->tokenId, $data[$this->fieldName])) {
+ if (!isset($data[$this->fieldName]) || !$this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName]))) {
$errorMessage = $this->errorMessage;
if (null !== $this->translator) {
View
44 src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php
@@ -12,13 +12,17 @@
namespace Symfony\Component\Form\Extension\Csrf\Type;
use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\Form\Exception\UnexpectedTypeException;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfTokenManagerAdapter;
use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
-use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Translation\TranslatorInterface;
/**
@@ -27,9 +31,9 @@
class FormTypeCsrfExtension extends AbstractTypeExtension
{
/**
- * @var CsrfTokenGeneratorInterface
+ * @var CsrfTokenManagerInterface
*/
- private $defaultTokenGenerator;
+ private $defaultTokenManager;
/**
* @var Boolean
@@ -51,9 +55,15 @@ class FormTypeCsrfExtension extends AbstractTypeExtension
*/
private $translationDomain;
- public function __construct(CsrfTokenGeneratorInterface $defaultTokenGenerator, $defaultEnabled = true, $defaultFieldName = '_token', TranslatorInterface $translator = null, $translationDomain = null)
+ public function __construct($defaultTokenManager, $defaultEnabled = true, $defaultFieldName = '_token', TranslatorInterface $translator = null, $translationDomain = null)
{
- $this->defaultTokenGenerator = $defaultTokenGenerator;
+ if ($defaultTokenManager instanceof CsrfProviderInterface) {
+ $defaultTokenManager = new CsrfProviderAdapter($defaultTokenManager);
+ } elseif (!$defaultTokenManager instanceof CsrfTokenManagerInterface) {
+ throw new UnexpectedTypeException($defaultTokenManager, 'CsrfProviderInterface or CsrfTokenManagerInterface');
+ }
+
+ $this->defaultTokenManager = $defaultTokenManager;
$this->defaultEnabled = $defaultEnabled;
$this->defaultFieldName = $defaultFieldName;
$this->translator = $translator;
@@ -75,7 +85,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
$builder
->addEventSubscriber(new CsrfValidationListener(
$options['csrf_field_name'],
- $options['csrf_token_generator'],
+ $options['csrf_token_manager'],
$options['csrf_token_id'],
$options['csrf_message'],
$this->translator,
@@ -95,7 +105,7 @@ public function finishView(FormView $view, FormInterface $form, array $options)
{
if ($options['csrf_protection'] && !$view->parent && $options['compound']) {
$factory = $form->getConfig()->getFormFactory();
- $data = $options['csrf_token_generator']->generateCsrfToken($options['csrf_token_id']);
+ $data = (string) $options['csrf_token_manager']->getToken($options['csrf_token_id']);
$csrfForm = $factory->createNamed($options['csrf_field_name'], 'hidden', $data, array(
'mapped' => false,
@@ -116,18 +126,20 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
};
// BC clause for the "csrf_provider" option
- $csrfTokenGenerator = function (Options $options) {
- return $options['csrf_provider'];
+ $csrfTokenManager = function (Options $options) {
+ return $options['csrf_provider'] instanceof CsrfTokenManagerAdapter
+ ? $options['csrf_provider']->getTokenManager()
+ : new CsrfProviderAdapter($options['csrf_provider']);
};
$resolver->setDefaults(array(
- 'csrf_protection' => $this->defaultEnabled,
- 'csrf_field_name' => $this->defaultFieldName,
- 'csrf_message' => 'The CSRF token is invalid. Please try to resubmit the form.',
- 'csrf_token_generator' => $csrfTokenGenerator,
- 'csrf_token_id' => $csrfTokenId,
- 'csrf_provider' => $this->defaultTokenGenerator,
- 'intention' => 'unknown',
+ 'csrf_protection' => $this->defaultEnabled,
+ 'csrf_field_name' => $this->defaultFieldName,
+ 'csrf_message' => 'The CSRF token is invalid. Please try to resubmit the form.',
+ 'csrf_token_manager' => $csrfTokenManager,
+ 'csrf_token_id' => $csrfTokenId,
+ 'csrf_provider' => new CsrfTokenManagerAdapter($this->defaultTokenManager),
+ 'intention' => 'unknown',
));
}
View
15 src/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php
@@ -12,8 +12,11 @@
namespace Symfony\Component\Form\Extension\Templating;
use Symfony\Component\Form\AbstractExtension;
+use Symfony\Component\Form\Exception\UnexpectedTypeException;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\Form\FormRenderer;
-use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Templating\PhpEngine;
use Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper;
@@ -24,10 +27,16 @@
*/
class TemplatingExtension extends AbstractExtension
{
- public function __construct(PhpEngine $engine, CsrfTokenGeneratorInterface $csrfTokenGenerator = null, array $defaultThemes = array())
+ public function __construct(PhpEngine $engine, $csrfTokenManager = null, array $defaultThemes = array())
{
+ if ($csrfTokenManager instanceof CsrfProviderInterface) {
+ $csrfTokenManager = new CsrfProviderAdapter($csrfTokenManager);
+ } elseif (null !== $csrfTokenManager && !$csrfTokenManager instanceof CsrfTokenManagerInterface) {
+ throw new UnexpectedTypeException($csrfTokenManager, 'CsrfProviderInterface or CsrfTokenManagerInterface');
+ }
+
$engine->addHelpers(array(
- new FormHelper(new FormRenderer(new TemplatingRendererEngine($engine, $defaultThemes), $csrfTokenGenerator))
+ new FormHelper(new FormRenderer(new TemplatingRendererEngine($engine, $defaultThemes), $csrfTokenManager))
));
}
}
View
22 src/Symfony/Component/Form/FormRenderer.php
@@ -13,7 +13,7 @@
use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Exception\BadMethodCallException;
-use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
/**
* Renders a form into HTML using a rendering engine.
@@ -30,9 +30,9 @@ class FormRenderer implements FormRendererInterface
private $engine;
/**
- * @var CsrfTokenGeneratorInterface
+ * @var CsrfTokenManagerInterface
*/
- private $csrfTokenGenerator;
+ private $csrfTokenManager;
/**
* @var array
@@ -49,10 +49,16 @@ class FormRenderer implements FormRendererInterface
*/
private $variableStack = array();
- public function __construct(FormRendererEngineInterface $engine, CsrfTokenGeneratorInterface $csrfTokenGenerator = null)
+ public function __construct(FormRendererEngineInterface $engine, $csrfTokenManager = null)
{
+ if ($csrfTokenManager instanceof CsrfProviderInterface) {
+ $csrfTokenManager = new CsrfProviderAdapter($csrfTokenManager);
+ } elseif (null !== $csrfTokenManager && !$csrfTokenManager instanceof CsrfTokenManagerInterface) {
+ throw new UnexpectedTypeException($csrfTokenManager, 'CsrfProviderInterface or CsrfTokenManagerInterface');
+ }
+
$this->engine = $engine;
- $this->csrfTokenGenerator = $csrfTokenGenerator;
+ $this->csrfTokenManager = $csrfTokenManager;
}
/**
@@ -76,11 +82,11 @@ public function setTheme(FormView $view, $themes)
*/
public function renderCsrfToken($tokenId)
{
- if (null === $this->csrfTokenGenerator) {
- throw new BadMethodCallException('CSRF tokens can only be generated if a CsrfTokenGeneratorInterface is injected in FormRenderer::__construct().');
+ if (null === $this->csrfTokenManager) {
+ throw new BadMethodCallException('CSRF tokens can only be generated if a CsrfTokenManagerInterface is injected in FormRenderer::__construct().');
}
- return $this->csrfTokenGenerator->generateCsrfToken($tokenId);
+ return $this->csrfTokenManager->getToken($tokenId)->getValue();
}
/**
View
7 src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php
@@ -13,6 +13,7 @@
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\Tests\Fixtures\AlternatingRowType;
+use Symfony\Component\Security\Csrf\CsrfToken;
abstract class AbstractDivLayoutTest extends AbstractLayoutTest
{
@@ -471,9 +472,9 @@ public function testNestedFormError()
public function testCsrf()
{
- $this->csrfTokenGenerator->expects($this->any())
- ->method('generateCsrfToken')
- ->will($this->returnValue('foo&bar'));
+ $this->csrfTokenManager->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue(new CsrfToken('token_id', 'foo&bar')));
$form = $this->factory->createNamedBuilder('name', 'form')
->add($this->factory
View
8 src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
@@ -17,7 +17,7 @@
abstract class AbstractLayoutTest extends \Symfony\Component\Form\Test\FormIntegrationTestCase
{
- protected $csrfTokenGenerator;
+ protected $csrfTokenManager;
protected function setUp()
{
@@ -27,21 +27,21 @@ protected function setUp()
\Locale::setDefault('en');
- $this->csrfTokenGenerator = $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface');
+ $this->csrfTokenManager = $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface');
parent::setUp();
}
protected function getExtensions()
{
return array(
- new CsrfExtension($this->csrfTokenGenerator),
+ new CsrfExtension($this->csrfTokenManager),
);
}
protected function tearDown()
{
- $this->csrfTokenGenerator = null;
+ $this->csrfTokenManager = null;
parent::tearDown();
}
View
7 src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests;
use Symfony\Component\Form\FormError;
+use Symfony\Component\Security\Csrf\CsrfToken;
abstract class AbstractTableLayoutTest extends AbstractLayoutTest
{
@@ -336,9 +337,9 @@ public function testNestedFormError()
public function testCsrf()
{
- $this->csrfTokenGenerator->expects($this->any())
- ->method('generateCsrfToken')
- ->will($this->returnValue('foo&bar'));
+ $this->csrfTokenManager->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue(new CsrfToken('token_id', 'foo&bar')));
$form = $this->factory->createNamedBuilder('name', 'form')
->add($this->factory
View
8 src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php
@@ -19,14 +19,14 @@ class CsrfValidationListenerTest extends \PHPUnit_Framework_TestCase
{
protected $dispatcher;
protected $factory;
- protected $tokenGenerator;
+ protected $tokenManager;
protected $form;
protected function setUp()
{
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
- $this->tokenGenerator = $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface');
+ $this->tokenManager = $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface');
$this->form = $this->getBuilder('post')
->setDataMapper($this->getDataMapper())
->getForm();
@@ -36,7 +36,7 @@ protected function tearDown()
{
$this->dispatcher = null;
$this->factory = null;
- $this->tokenGenerator = null;
+ $this->tokenManager = null;
$this->form = null;
}
@@ -66,7 +66,7 @@ public function testStringFormData()
$data = "XP4HUzmHPi";
$event = new FormEvent($this->form, $data);
- $validation = new CsrfValidationListener('csrf', $this->tokenGenerator, 'unknown', 'Invalid.');
+ $validation = new CsrfValidationListener('csrf', $this->tokenManager, 'unknown', 'Invalid.');
$validation->preSubmit($event);
// Validate accordingly
View
65 src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php
@@ -16,6 +16,7 @@
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\Form\Extension\Csrf\CsrfExtension;
+use Symfony\Component\Security\Csrf\CsrfToken;
class FormTypeCsrfExtensionTest_ChildType extends AbstractType
{
@@ -37,7 +38,7 @@ class FormTypeCsrfExtensionTest extends TypeTestCase
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
- protected $tokenGenerator;
+ protected $tokenManager;
/**
* @var \PHPUnit_Framework_MockObject_MockObject
@@ -46,15 +47,15 @@ class FormTypeCsrfExtensionTest extends TypeTestCase
protected function setUp()
{
- $this->tokenGenerator = $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface');
+ $this->tokenManager = $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface');
$this->translator = $this->getMock('Symfony\Component\Translation\TranslatorInterface');
parent::setUp();
}
protected function tearDown()
{
- $this->tokenGenerator = null;
+ $this->tokenManager = null;
$this->translator = null;
parent::tearDown();
@@ -63,7 +64,7 @@ protected function tearDown()
protected function getExtensions()
{
return array_merge(parent::getExtensions(), array(
- new CsrfExtension($this->tokenGenerator, $this->translator),
+ new CsrfExtension($this->tokenManager, $this->translator),
));
}
@@ -123,16 +124,16 @@ public function testCsrfProtectionCanBeDisabled()
public function testGenerateCsrfToken()
{
- $this->tokenGenerator->expects($this->once())
- ->method('generateCsrfToken')
- ->with('%INTENTION%')
- ->will($this->returnValue('token'));
+ $this->tokenManager->expects($this->once())
+ ->method('getToken')
+ ->with('TOKEN_ID')
+ ->will($this->returnValue(new CsrfToken('TOKEN_ID', 'token')));
$view = $this->factory
->create('form', null, array(
'csrf_field_name' => 'csrf',
- 'csrf_provider' => $this->tokenGenerator,
- 'intention' => '%INTENTION%',
+ 'csrf_token_manager' => $this->tokenManager,
+ 'csrf_token_id' => 'TOKEN_ID',
'compound' => true,
))
->createView();
@@ -153,16 +154,16 @@ public function provideBoolean()
*/
public function testValidateTokenOnSubmitIfRootAndCompound($valid)
{
- $this->tokenGenerator->expects($this->once())
- ->method('isCsrfTokenValid')
- ->with('%INTENTION%', 'token')
+ $this->tokenManager->expects($this->once())
+ ->method('isTokenValid')
+ ->with(new CsrfToken('TOKEN_ID', 'token'))
->will($this->returnValue($valid));
$form = $this->factory
->createBuilder('form', null, array(
'csrf_field_name' => 'csrf',
- 'csrf_provider' => $this->tokenGenerator,
- 'intention' => '%INTENTION%',
+ 'csrf_token_manager' => $this->tokenManager,
+ 'csrf_token_id' => 'TOKEN_ID',
'compound' => true,
))
->add('child', 'text')
@@ -182,14 +183,14 @@ public function testValidateTokenOnSubmitIfRootAndCompound($valid)
public function testFailIfRootAndCompoundAndTokenMissing()
{
- $this->tokenGenerator->expects($this->never())
- ->method('isCsrfTokenValid');
+ $this->tokenManager->expects($this->never())
+ ->method('isTokenValid');
$form = $this->factory
->createBuilder('form', null, array(
'csrf_field_name' => 'csrf',
- 'csrf_provider' => $this->tokenGenerator,
- 'intention' => '%INTENTION%',
+ 'csrf_token_manager' => $this->tokenManager,
+ 'csrf_token_id' => 'TOKEN_ID',
'compound' => true,
))
->add('child', 'text')
@@ -209,16 +210,16 @@ public function testFailIfRootAndCompoundAndTokenMissing()
public function testDontValidateTokenIfCompoundButNoRoot()
{
- $this->tokenGenerator->expects($this->never())
- ->method('isCsrfTokenValid');
+ $this->tokenManager->expects($this->never())
+ ->method('isTokenValid');
$form = $this->factory
->createNamedBuilder('root', 'form')
->add($this->factory
->createNamedBuilder('form', 'form', null, array(
'csrf_field_name' => 'csrf',
- 'csrf_provider' => $this->tokenGenerator,
- 'intention' => '%INTENTION%',
+ 'csrf_token_manager' => $this->tokenManager,
+ 'csrf_token_id' => 'TOKEN_ID',
'compound' => true,
))
)
@@ -233,14 +234,14 @@ public function testDontValidateTokenIfCompoundButNoRoot()
public function testDontValidateTokenIfRootButNotCompound()
{
- $this->tokenGenerator->expects($this->never())
- ->method('isCsrfTokenValid');
+ $this->tokenManager->expects($this->never())
+ ->method('isTokenValid');
$form = $this->factory
->create('form', null, array(
'csrf_field_name' => 'csrf',
- 'csrf_provider' => $this->tokenGenerator,
- 'intention' => '%INTENTION%',
+ 'csrf_token_manager' => $this->tokenManager,
+ 'csrf_token_id' => 'TOKEN_ID',
'compound' => false,
));
@@ -269,9 +270,9 @@ public function testNoCsrfProtectionOnPrototype()
public function testsTranslateCustomErrorMessage()
{
- $this->tokenGenerator->expects($this->once())
- ->method('isCsrfTokenValid')
- ->with('%INTENTION%', 'token')
+ $this->tokenManager->expects($this->once())
+ ->method('isTokenValid')
+ ->with(new CsrfToken('TOKEN_ID', 'token'))
->will($this->returnValue(false));
$this->translator->expects($this->once())
@@ -282,9 +283,9 @@ public function testsTranslateCustomErrorMessage()
$form = $this->factory
->createBuilder('form', null, array(
'csrf_field_name' => 'csrf',
- 'csrf_provider' => $this->tokenGenerator,
+ 'csrf_token_manager' => $this->tokenManager,
'csrf_message' => 'Foobar',
- 'intention' => '%INTENTION%',
+ 'csrf_token_id' => 'TOKEN_ID',
'compound' => true,
))
->getForm();
View
21 src/Symfony/Component/Security/Core/Exception/ExceptionInterface.php
@@ -0,0 +1,21 @@
+<?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\Security\Core\Exception;
+
+/**
+ * Base ExceptionInterface for the Security component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface ExceptionInterface
+{
+}
View
21 src/Symfony/Component/Security/Core/Exception/InvalidArgumentException.php
@@ -0,0 +1,21 @@
+<?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\Security\Core\Exception;
+
+/**
+ * Base InvalidArgumentException for the Security component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
View
21 src/Symfony/Component/Security/Core/Exception/RuntimeException.php
@@ -0,0 +1,21 @@
+<?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\Security\Core\Exception;
+
+/**
+ * Base RuntimeException for the Security component.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
View
66 src/Symfony/Component/Security/Csrf/CsrfToken.php
@@ -0,0 +1,66 @@
+<?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\Security\Csrf;
+
+/**
+ * A CSRF token.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class CsrfToken
+{
+ /**
+ * @var string
+ */
+ private $id;
+
+ /**
+ * @var string
+ */
+ private $value;
+
+ public function __construct($id, $value)
+ {
+ $this->id = (string) $id;
+ $this->value = (string) $value;
+ }
+
+ /**
+ * Returns the ID of the CSRF token.
+ *
+ * @return string The token ID
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Returns the value of the CSRF token.
+ *
+ * @return string The token value
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Returns the value of the CSRF token.
+ *
+ * @return string The token value.
+ */
+ public function __toString()
+ {
+ return $this->value;
+ }
+}
View
105 src/Symfony/Component/Security/Csrf/CsrfTokenGenerator.php
@@ -1,105 +0,0 @@
-<?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\Security\Csrf;
-
-use Symfony\Component\Security\Core\Util\SecureRandomInterface;
-use Symfony\Component\Security\Core\Util\SecureRandom;
-use Symfony\Component\Security\Core\Util\StringUtils;
-use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage;
-use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface;
-
-/**
- * Generates and validates CSRF tokens.
- *
- * @since 2.4
- * @author Bernhard Schussek <bernhard.schussek@symfony.com>
- */
-class CsrfTokenGenerator implements CsrfTokenGeneratorInterface
-{
- /**
- * The entropy of the token in bits.
- * @var integer
- */
- const TOKEN_ENTROPY = 256;
-
- /**
- * @var TokenStorageInterface
- */
- private $storage;
-
- /**
- * The generator for random values.
- * @var SecureRandomInterface
- */
- private $random;
-
- /**
- * Creates a new CSRF provider using PHP's native session storage.
- *
- * @param TokenStorageInterface $storage The storage for storing generated
- * CSRF tokens
- * @param SecureRandomInterface $random The used random value generator
- * @param integer $entropy The amount of entropy collected for
- * newly generated tokens (in bits)
- *
- */
- public function __construct(TokenStorageInterface $storage = null, SecureRandomInterface $random = null, $entropy = self::TOKEN_ENTROPY)
- {
- if (null === $storage) {
- $storage = new NativeSessionTokenStorage();
- }
-
- if (null === $random) {
- $random = new SecureRandom();
- }
-
- $this->storage = $storage;
- $this->random = $random;
- $this->entropy = $entropy;
- }
-
- /**
- * {@inheritDoc}
- */
- public function generateCsrfToken($tokenId)
- {
- $currentToken = $this->storage->getToken($tokenId, false);
-
- // Token exists and is still valid
- if (false !== $currentToken) {
- return $currentToken;
- }
-
- // Token needs to be (re)generated
- // Generate an URI safe base64 encoded string that does not contain "+",
- // "/" or "=" which need to be URL encoded and make URLs unnecessarily
- // longer.
- $bytes = $this->random->nextBytes($this->entropy / 8);
- $token = rtrim(strtr(base64_encode($bytes), '+/', '-_'), '=');
-
- $this->storage->setToken($tokenId, $token);
-
- return $token;
- }
-
- /**
- * {@inheritDoc}
- */
- public function isCsrfTokenValid($tokenId, $token)
- {
- if (!$this->storage->hasToken($tokenId)) {
- return false;
- }
-
- return StringUtils::equals((string) $this->storage->getToken($tokenId), $token);
- }
-}
View
106 src/Symfony/Component/Security/Csrf/CsrfTokenManager.php
@@ -0,0 +1,106 @@
+<?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\Security\Csrf;
+
+use Symfony\Component\Security\Core\Util\StringUtils;
+use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator;
+use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
+use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage;
+use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface;
+
+/**
+ * Default implementation of {@link CsrfTokenManagerInterface}.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class CsrfTokenManager implements CsrfTokenManagerInterface
+{
+ /**
+ * @var TokenGeneratorInterface
+ */
+ private $generator;
+
+ /**
+ * @var TokenStorageInterface
+ */
+ private $storage;
+
+ /**
+ * Creates a new CSRF provider using PHP's native session storage.
+ *
+ * @param TokenGeneratorInterface $generator The token generator
+ * @param TokenStorageInterface $storage The storage for storing
+ * generated CSRF tokens
+ *
+ */
+ public function __construct(TokenGeneratorInterface $generator = null, TokenStorageInterface $storage = null)
+ {
+ if (null === $generator) {
+ $generator = new UriSafeTokenGenerator();
+ }
+
+ if (null === $storage) {
+ $storage = new NativeSessionTokenStorage();
+ }
+
+ $this->generator = $generator;
+ $this->storage = $storage;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getToken($tokenId)
+ {
+ if ($this->storage->hasToken($tokenId)) {
+ $value = $this->storage->getToken($tokenId);
+ } else {
+ $value = $this->generator->generateToken();
+
+ $this->storage->setToken($tokenId, $value);
+ }
+
+ return new CsrfToken($tokenId, $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function refreshToken($tokenId)
+ {
+ $value = $this->generator->generateToken();
+
+ $this->storage->setToken($tokenId, $value);
+
+ return new CsrfToken($tokenId, $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeToken($tokenId)
+ {
+ return $this->storage->removeToken($tokenId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isTokenValid(CsrfToken $token)
+ {
+ if (!$this->storage->hasToken($token->getId())) {
+ return false;
+ }
+
+ return StringUtils::equals((string) $this->storage->getToken($token->getId()), $token->getValue());
+ }
+}
View
67 src/Symfony/Component/Security/Csrf/CsrfTokenManagerInterface.php
@@ -0,0 +1,67 @@
+<?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\Security\Csrf;
+
+/**
+ * Manages CSRF tokens.
+ *
+ * @since 2.4
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface CsrfTokenManagerInterface
+{
+ /**
+ * Returns a CSRF token for the given ID.
+ *
+ * If previously no token existed for the given ID, a new token is
+ * generated. Otherwise the existing token is returned.
+ *
+ * @param string $tokenId The token ID. You may choose an arbitrary value
+ * for the ID
+ *
+ * @return CsrfToken The CSRF token
+ */
+ public function getToken($tokenId);
+
+ /**
+ * Generates a new token value for the given ID.
+ *
+ * This method will generate a new token for the given token ID, independent
+ * of whether a token value previously existed or not. It can be used to
+ * enforce once-only tokens in environments with high security needs.
+ *
+ * @param string $tokenId The token ID. You may choose an arbitrary value
+ * for the ID
+ *
+ * @return CsrfToken The CSRF token
+ */
+ public function refreshToken($tokenId);
+
+ /**
+ * Invalidates the CSRF token with the given ID, if one exists.
+ *
+ * @param string $tokenId The token ID
+ *
+ * @return Boolean Returns true if a token existed for this ID, false
+ * otherwise
+ */
+ public function removeToken($tokenId);
+
+ /**
+ * Returns whether the given CSRF token is valid.
+ *
+ * @param CsrfToken $token A CSRF token
+ *
+ * @return Boolean Returns true if the token is valid, false otherwise
+ */
+ public function isTokenValid(CsrfToken $token);
+}
View
21 src/Symfony/Component/Security/Csrf/Exception/TokenNotFoundException.php
@@ -0,0 +1,21 @@
+<?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\Security\Csrf\Exception;
+
+use Symfony\Component\Security\Core\Exception\RuntimeException;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class TokenNotFoundException extends RuntimeException
+{
+}
View
2 src/Symfony/Component/Security/Csrf/README.md
@@ -2,7 +2,7 @@ Security Component - CSRF
=========================
The Security CSRF (cross-site request forgery) component provides a class
-`CsrfTokenGenerator` for generating and validating CSRF tokens.
+`CsrfTokenManager` for generating and validating CSRF tokens.
Resources
---------
View
148 src/Symfony/Component/Security/Csrf/Tests/CsrfTokenGeneratorTest.php
@@ -1,148 +0,0 @@
-<?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\CsrfTokenGenerator;
-
-/**
- * @author Bernhard Schussek <bschussek@gmail.com>
- */
-class CsrfTokenGeneratorTest extends \PHPUnit_Framework_TestCase
-{
- /**
- * A non alpha-numeric byte string
- * @var string
- */
- private static $bytes;
-
- /**
- * @var \PHPUnit_Framework_MockObject_MockObject
- */
- private $random;
-
- /**
- * @var \PHPUnit_Framework_MockObject_MockObject
- */
- private $storage;
-
- /**
- * @var CsrfTokenGenerator
- */
- private $generator;
-
- public static function setUpBeforeClass()
- {
- self::$bytes = base64_decode('aMf+Tct/RLn2WQ==');
- }
-
- protected function setUp()
- {
- $this->random = $this->getMock('Symfony\Component\Security\Core\Util\SecureRandomInterface');
- $this->storage = $this->getMock('Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface');
- $this->generator = new CsrfTokenGenerator($this->storage, $this->random);
- }
-
- protected function tearDown()
- {
- $this->random = null;
- $this->storage = null;
- $this->generator = null;
- }
-
- public function testGenerateNewToken()
- {
- $this->storage->expects($this->once())
- ->method('getToken')
- ->with('token_id', false)
- ->will($this->returnValue(false));
-
- $this->storage->expects($this->once())
- ->method('setToken')
- ->with('token_id', $this->anything())
- ->will($this->returnCallback(function ($tokenId, $token) use (&$storedToken) {
- $storedToken = $token;
- }));
-
- $this->random->expects($this->once())
- ->method('nextBytes')
- ->will($this->returnValue(self::$bytes));
-
- $token = $this->generator->generateCsrfToken('token_id');
-
- $this->assertSame($token, $storedToken);
- $this->assertTrue(ctype_print($token), 'is printable');
- $this->assertStringNotMatchesFormat('%S+%S', $token, 'is URI safe');
- $this->assertStringNotMatchesFormat('%S/%S', $token, 'is URI safe');
- $this->assertStringNotMatchesFormat('%S=%S', $token, 'is URI safe');
- }
-
- public function testUseExistingTokenIfAvailable()
- {
- $this->storage->expects($this->once())
- ->method('getToken')
- ->with('token_id', false)
- ->will($this->returnValue('TOKEN'));
-
- $this->storage->expects($this->never())
- ->method('setToken');
-
- $this->random->expects($this->never())
- ->method('nextBytes');
-
- $token = $this->generator->generateCsrfToken('token_id');
-
- $this->assertEquals('TOKEN', $token);
- }
-
- public function testMatchingTokenIsValid()
- {
- $this->storage->expects($this->once())
- ->method('hasToken')
- ->with('token_id')
- ->will($this->returnValue(true));
-
- $this->storage->expects($this->once())
- ->method('getToken')
- ->with('token_id')
- ->will($this->returnValue('TOKEN'));
-
- $this->assertTrue($this->generator->isCsrfTokenValid('token_id', 'TOKEN'));
- }
-
- public function testNonMatchingTokenIsNotValid()
- {
- $this->storage->expects($this->once())
- ->method('hasToken')
- ->with('token_id')
- ->will($this->returnValue(true));
-
- $this->storage->expects($this->once())
- ->method('getToken')
- ->with('token_id')
- ->will($this->returnValue('TOKEN'));
-
- $this->assertFalse($this->generator->isCsrfTokenValid('token_id', 'FOOBAR'));
- }
-
- public function testNonExistingTokenIsNotValid()
- {
- $this->storage->expects($this->once())
- ->method('hasToken')
- ->with('token_id')
- ->will($this->returnValue(false));
-
- $this->storage->expects($this->never())
- ->method('getToken');
-
- $this->assertFalse($this->generator->isCsrfTokenValid('token_id', 'FOOBAR'));
- }
-}
View
164 src/Symfony/Component/Security/Csrf/Tests/CsrfTokenManagerTest.php
@@ -0,0 +1,164 @@
+<?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\CsrfToken;
+use Symfony\Component\Security\Csrf\CsrfTokenManager;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class CsrfTokenManagerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $generator;
+
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $storage;
+
+ /**
+ * @var CsrfTokenManager
+ */
+ private $manager;
+
+ protected function setUp()
+ {
+ $this->generator = $this->getMock('Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface');
+ $this->storage = $this->getMock('Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface');
+ $this->manager = new CsrfTokenManager($this->generator, $this->storage);
+ }
+
+ protected function tearDown()
+ {
+ $this->generator = null;
+ $this->storage = null;
+ $this->manager = null;
+ }
+
+ public function testGetNonExistingToken()
+ {
+ $this->storage->expects($this->once())
+ ->method('hasToken')
+ ->with('token_id')
+ ->will($this->returnValue(false));
+
+ $this->generator->expects($this->once())
+ ->method('generateToken')
+ ->will($this->returnValue('TOKEN'));
+
+ $this->storage->expects($this->once())
+ ->method('setToken')
+ ->with('token_id', 'TOKEN');
+
+ $token = $this->manager->getToken('token_id');
+
+ $this->assertInstanceOf('Symfony\Component\Security\Csrf\CsrfToken', $token);
+ $this->assertSame('token_id', $token->getId());
+ $this->assertSame('TOKEN', $token->getValue());
+ }
+
+ public function testUseExistingTokenIfAvailable()
+ {
+ $this->storage->expects($this->once())
+ ->method('hasToken')
+ ->with('token_id')
+ ->will($this->returnValue(true));
+
+ $this->storage->expects($this->once())
+ ->method('getToken')
+ ->with('token_id')
+ ->will($this->returnValue('TOKEN'));
+
+ $token = $this->manager->getToken('token_id');
+
+ $this->assertInstanceOf('Symfony\Component\Security\Csrf\CsrfToken', $token);
+ $this->assertSame('token_id', $token->getId());
+ $this->assertSame('TOKEN', $token->getValue());
+ }
+
+ public function testRefreshTokenAlwaysReturnsNewToken()
+ {
+ $this->storage->expects($this->never())
+ ->method('hasToken');
+
+ $this->generator->expects($this->once())
+ ->method('generateToken')
+ ->will($this->returnValue('TOKEN'));
+
+ $this->storage->expects($this->once())
+ ->method('setToken')
+ ->with('token_id', 'TOKEN');
+
+ $token = $this->manager->refreshToken('token_id');
+
+ $this->assertInstanceOf('Symfony\Component\Security\Csrf\CsrfToken', $token);
+ $this->assertSame('token_id', $token->getId());
+ $this->assertSame('TOKEN', $token->getValue());
+ }
+
+ public function testMatchingTokenIsValid()
+ {
+ $this->storage->expects($this->once())
+ ->method('hasToken')
+ ->with('token_id')
+ ->will($this->returnValue(true));
+
+ $this->storage->expects($this->once())
+ ->method('getToken')
+ ->with('token_id')
+ ->will($this->returnValue('TOKEN'));
+
+ $this->assertTrue($this->manager->isTokenValid(new CsrfToken('token_id', 'TOKEN')));
+ }
+
+ public function testNonMatchingTokenIsNotValid()
+ {
+ $this->storage->expects($this->once())
+ ->method('hasToken')
+ ->with('token_id')
+ ->will($this->returnValue(true));
+
+ $this->storage->expects($this->once())
+ ->method('getToken')
+ ->with('token_id')
+ ->will($this->returnValue('TOKEN'));
+
+ $this->assertFalse($this->manager->isTokenValid(new CsrfToken('token_id', 'FOOBAR')));
+ }
+
+ public function testNonExistingTokenIsNotValid()
+ {
+ $this->storage->expects($this->once())
+ ->method('hasToken')
+ ->with('token_id')
+ ->will($this->returnValue(false));
+
+ $this->storage->expects($this->never())
+ ->method('getToken');
+
+ $this->assertFalse($this->manager->isTokenValid(new CsrfToken('token_id', 'FOOBAR')));
+ }
+
+ public function testRemoveToken()
+ {
+ $this->storage->expects($this->once())
+ ->method('removeToken')
+ ->with('token_id')
+ ->will($this->returnValue('REMOVED_TOKEN'));
+
+ $this->assertSame('REMOVED_TOKEN', $this->manager->removeToken('token_id'));
+ }
+}
View
71 src/Symfony/Component/Security/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php
@@ -0,0 +1,71 @@
+<?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\TokenGenerator;
+
+use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class UriSafeTokenGeneratorTest extends \PHPUnit_Framework_TestCase
+{
+ const ENTROPY = 1000;
+
+ /**
+ * A non alpha-numeric byte string
+ * @var string
+ */
+ private static $bytes;
+
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $rand