diff --git a/doc/bc/changes-6.0.md b/doc/bc/changes-6.0.md index 7b911bc19fa..129804e9851 100644 --- a/doc/bc/changes-6.0.md +++ b/doc/bc/changes-6.0.md @@ -145,6 +145,11 @@ Changes affecting version compatibility with former or future versions. Before, it contained the physical path to the file, e.g. `var/site/storage/original/...`. Since this path isn't allowed to pass through the rewrite rules for security, it was not usable. This also affects REST, that will now expose a valid HTTP download URL. + +* `csrf_token` variable is not passed to login template any more. Use `csrf_token()` Twig function to generate it instead. + ```jinja + + ``` ## Deprecations diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml index 09d45dad537..21ef5910652 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml +++ b/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml @@ -33,7 +33,7 @@ services: ezpublish.security.controller: class: %ezpublish.security.controller.class% - arguments: [@templating, @ezpublish.config.resolver, @?form.csrf_provider] + arguments: [@templating, @ezpublish.config.resolver, @security.authentication_utils] ezpublish.security.login_listener: class: %ezpublish.security.login_listener.class% diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig b/eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig index 3854baa5956..8111f4ec6fa 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig +++ b/eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig @@ -22,7 +22,7 @@ - + {# If you want to control the URL the user diff --git a/eZ/Bundle/EzPublishRestBundle/EventListener/CsrfListener.php b/eZ/Bundle/EzPublishRestBundle/EventListener/CsrfListener.php index 53c5ad0a3ba..ee0b42592f2 100644 --- a/eZ/Bundle/EzPublishRestBundle/EventListener/CsrfListener.php +++ b/eZ/Bundle/EzPublishRestBundle/EventListener/CsrfListener.php @@ -13,9 +13,10 @@ use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; -use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; use eZ\Bundle\EzPublishRestBundle\RestEvents; +use Symfony\Component\Security\Csrf\CsrfToken; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; class CsrfListener implements EventSubscriberInterface { @@ -25,9 +26,9 @@ class CsrfListener implements EventSubscriberInterface const CSRF_TOKEN_HEADER = "X-CSRF-Token"; /** - * @var \Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface + * @var null|CsrfTokenManagerInterface */ - private $csrfProvider; + private $csrfTokenManager; /** * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface @@ -51,19 +52,19 @@ class CsrfListener implements EventSubscriberInterface * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher * @param bool $csrfEnabled * @param string $csrfTokenIntention - * @param null|\Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface $csrfProvider + * @param null|CsrfTokenManagerInterface $csrfTokenManager */ public function __construct( EventDispatcherInterface $eventDispatcher, $csrfEnabled, $csrfTokenIntention, - CsrfProviderInterface $csrfProvider = null + CsrfTokenManagerInterface $csrfTokenManager = null ) { $this->eventDispatcher = $eventDispatcher; $this->csrfEnabled = $csrfEnabled; $this->csrfTokenIntention = $csrfTokenIntention; - $this->csrfProvider = $csrfProvider; + $this->csrfTokenManager = $csrfTokenManager; } /** @@ -138,8 +139,7 @@ protected function isMethodSafe( $method ) */ protected function isLoginRequest( $route ) { - // TODO: add CSRF token to protect against force-login attacks - return $route == "ezpublish_rest_createSession"; + return $route === "ezpublish_rest_createSession"; } /** @@ -154,9 +154,11 @@ protected function checkCsrfToken( Request $request ) return false; } - return $this->csrfProvider->isCsrfTokenValid( - $this->csrfTokenIntention, - $request->headers->get( self::CSRF_TOKEN_HEADER ) + return $this->csrfTokenManager->isTokenValid( + new CsrfToken( + $this->csrfTokenIntention, + $request->headers->get( self::CSRF_TOKEN_HEADER ) + ) ); } } diff --git a/eZ/Bundle/EzPublishRestBundle/Resources/config/services.yml b/eZ/Bundle/EzPublishRestBundle/Resources/config/services.yml index 0d3a0a1e992..07d574bfbd7 100644 --- a/eZ/Bundle/EzPublishRestBundle/Resources/config/services.yml +++ b/eZ/Bundle/EzPublishRestBundle/Resources/config/services.yml @@ -230,7 +230,7 @@ services: - @event_dispatcher - %form.type_extension.csrf.enabled% - %ezpublish_rest.csrf_token_intention% - - @?form.csrf_provider + - @?security.csrf.token_manager tags: - { name: kernel.event_subscriber } diff --git a/eZ/Bundle/EzPublishRestBundle/Tests/EventListener/CsrfListenerTest.php b/eZ/Bundle/EzPublishRestBundle/Tests/EventListener/CsrfListenerTest.php index 081d9542beb..dc08de6de86 100644 --- a/eZ/Bundle/EzPublishRestBundle/Tests/EventListener/CsrfListenerTest.php +++ b/eZ/Bundle/EzPublishRestBundle/Tests/EventListener/CsrfListenerTest.php @@ -21,6 +21,7 @@ use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; use eZ\Bundle\EzPublishRestBundle\EventListener\CsrfListener; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Security\Csrf\CsrfToken; class CsrfListenerTest extends EventListenerTest { @@ -162,19 +163,23 @@ public function testValidToken() */ protected function getCsrfProviderMock() { - $provider = $this->getMock( - 'Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface' - ); + $provider = $this->getMock( '\Symfony\Component\Security\Csrf\CsrfTokenManagerInterface' ); $provider->expects( $this->any() ) - ->method( 'isCsrfTokenValid' ) + ->method( 'isTokenValid' ) ->will( - $this->returnValueMap( - array( - array( self::INTENTION, self::VALID_TOKEN, true ), - array( self::INTENTION, self::INVALID_TOKEN, false ) - ) + $this->returnCallback( + function ( CsrfToken $token ) + { + if ( $token == new CsrfToken( self::INTENTION, self::VALID_TOKEN ) ) + { + return true; + } + + return false; + } ) ); + return $provider; } diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/SecurityController.php b/eZ/Publish/Core/MVC/Symfony/Controller/SecurityController.php index 32202b7fe18..b257af2c7fb 100644 --- a/eZ/Publish/Core/MVC/Symfony/Controller/SecurityController.php +++ b/eZ/Publish/Core/MVC/Symfony/Controller/SecurityController.php @@ -10,10 +10,8 @@ namespace eZ\Publish\Core\MVC\Symfony\Controller; use eZ\Publish\Core\MVC\ConfigResolverInterface; -use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Security\Core\Security; +use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; use Symfony\Component\Templating\EngineInterface; class SecurityController @@ -29,39 +27,25 @@ class SecurityController protected $configResolver; /** - * @var \Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface|null + * @var \Symfony\Component\Security\Http\Authentication\AuthenticationUtils */ - protected $csrfProvider; + protected $authenticationUtils; - public function __construct( EngineInterface $templateEngine, ConfigResolverInterface $configResolver, CsrfProviderInterface $csrfProvider = null ) + public function __construct( EngineInterface $templateEngine, ConfigResolverInterface $configResolver, AuthenticationUtils $authenticationUtils ) { $this->templateEngine = $templateEngine; $this->configResolver = $configResolver; - $this->csrfProvider = $csrfProvider; + $this->authenticationUtils = $authenticationUtils; } - public function loginAction( Request $request ) + public function loginAction() { - $session = $request->getSession(); - - if ( $request->attributes->has( Security::AUTHENTICATION_ERROR ) ) - { - $error = $request->attributes->get( Security::AUTHENTICATION_ERROR ); - } - else - { - $error = $session->get( Security::AUTHENTICATION_ERROR ); - $session->remove( Security::AUTHENTICATION_ERROR ); - } - - $csrfToken = isset( $this->csrfProvider ) ? $this->csrfProvider->generateCsrfToken( 'authenticate' ) : null; return new Response( $this->templateEngine->render( $this->configResolver->getParameter( 'security.login_template' ), array( - 'last_username' => $session->get( Security::LAST_USERNAME ), - 'error' => $error, - 'csrf_token' => $csrfToken, + 'last_username' => $this->authenticationUtils->getLastUsername(), + 'error' => $this->authenticationUtils->getLastAuthenticationError(), 'layout' => $this->configResolver->getParameter( 'security.base_layout' ), ) ) diff --git a/eZ/Publish/Core/REST/Server/Controller/User.php b/eZ/Publish/Core/REST/Server/Controller/User.php index 6822090ed4f..263780c1fd6 100644 --- a/eZ/Publish/Core/REST/Server/Controller/User.php +++ b/eZ/Publish/Core/REST/Server/Controller/User.php @@ -36,6 +36,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Csrf\CsrfToken; /** * User controller @@ -995,17 +996,19 @@ public function createSession( Request $request ) try { $csrfToken = ''; - $csrfProvider = $this->container->get( 'form.csrf_provider', ContainerInterface::NULL_ON_INVALID_REFERENCE ); + $csrfTokenManager = $this->container->get( 'security.csrf.token_manager', ContainerInterface::NULL_ON_INVALID_REFERENCE ); $session = $request->getSession(); if ( $session->isStarted() ) { - if ( $csrfProvider ) + if ( $csrfTokenManager ) { $csrfToken = $request->headers->get( 'X-CSRF-Token' ); if ( - !$csrfProvider->isCsrfTokenValid( - $this->container->getParameter( 'ezpublish_rest.csrf_token_intention' ), - $csrfToken + !$csrfTokenManager->isTokenValid( + new CsrfToken( + $this->container->getParameter( 'ezpublish_rest.csrf_token_intention' ), + $csrfToken + ) ) ) { @@ -1020,7 +1023,7 @@ public function createSession( Request $request ) // This will seamlessly start the session. if ( !$csrfToken ) { - $csrfToken = $csrfProvider->generateCsrfToken( + $csrfToken = $csrfTokenManager->generateCsrfToken( $this->container->getParameter( 'ezpublish_rest.csrf_token_intention' ) ); }