diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php index eef9aecdc4..6a56360806 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; +use Symfony\Component\DependencyInjection\Configuration\Builder\NodeBuilder; use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -27,6 +28,8 @@ class FormLoginFactory extends AbstractFactory { $this->addOption('username_parameter', '_username'); $this->addOption('password_parameter', '_password'); + $this->addOption('csrf_parameter', '_csrf_token'); + $this->addOption('csrf_page_id', 'form_login'); $this->addOption('post_only', true); } @@ -40,6 +43,15 @@ class FormLoginFactory extends AbstractFactory return 'form-login'; } + public function addConfiguration(NodeBuilder $builder) + { + parent::addConfiguration($builder); + + $builder + ->scalarNode('csrf_provider')->cannotBeEmpty()->end() + ; + } + protected function getListenerId() { return 'security.authentication.listener.form'; @@ -57,6 +69,20 @@ class FormLoginFactory extends AbstractFactory return $provider; } + protected function createListener($container, $id, $config, $userProvider) + { + $listenerId = parent::createListener($container, $id, $config, $userProvider); + + if (isset($config['csrf_provider'])) { + $container + ->getDefinition($listenerId) + ->addArgument(new Reference($config['csrf_provider'])) + ; + } + + return $listenerId; + } + protected function createEntryPoint($container, $id, $config, $defaultEntryPoint) { $entryPointId = 'security.authentication.form_entry_point.'.$id; diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 40f0abfe40..bdab6f9151 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -116,6 +116,6 @@ - + diff --git a/src/Symfony/Component/Security/Core/Exception/InvalidCsrfTokenException.php b/src/Symfony/Component/Security/Core/Exception/InvalidCsrfTokenException.php new file mode 100644 index 0000000000..f19bcbf7f0 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Exception/InvalidCsrfTokenException.php @@ -0,0 +1,12 @@ + + */ +class InvalidCsrfTokenException extends AuthenticationException +{ +} \ No newline at end of file diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index 1ba71baedd..0e3b396b0f 100644 --- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -11,15 +11,16 @@ namespace Symfony\Component\Security\Http\Firewall; +use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; - -use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; +use Symfony\Component\Security\Core\SecurityContextInterface; /** * UsernamePasswordFormAuthenticationListener is the default implementation of @@ -29,16 +30,22 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; */ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationListener { + protected $csrfProvider; + /** * {@inheritdoc} */ - public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null) + public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, CsrfProviderInterface $csrfProvider = null) { parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $providerKey, array_merge(array( 'username_parameter' => '_username', 'password_parameter' => '_password', + 'csrf_parameter' => '_csrf_token', + 'csrf_page_id' => 'form_login', 'post_only' => true, ), $options), $successHandler, $failureHandler, $logger); + + $this->csrfProvider = $csrfProvider; } /** @@ -54,6 +61,14 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL return null; } + if (null !== $this->csrfProvider) { + $csrfToken = $request->get($this->options['csrf_parameter']); + + if (false === $this->csrfProvider->isTokenValid($this->options['csrf_page_id'], $csrfToken)) { + throw new InvalidCsrfTokenException('Invalid CSRF token.'); + } + } + $username = trim($request->get($this->options['username_parameter'])); $password = $request->get($this->options['password_parameter']);