From 880dc52d3c680f4e6ff5cdd1e559babda48a264b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81ro=CC=82me=20Vieilledent?= Date: Thu, 13 Nov 2014 15:22:42 +0100 Subject: [PATCH] Fix EZP-23632: Exception when running eZ Publish behind built in reverse proxy --- .../EventListener/ConfigScopeListener.php | 42 +---- .../EventListener/DynamicSettingsListener.php | 85 +++++++++ .../EventListener/OriginalRequestListener.php | 53 ++++++ .../EventListener/RequestEventListener.php | 8 +- eZ/Bundle/EzPublishCoreBundle/HttpCache.php | 7 + .../Resources/config/helpers.yml | 7 + .../Resources/config/routing.yml | 7 +- .../Resources/config/routing/internal.yml | 3 + .../EventListener/ConfigScopeListenerTest.php | 53 +----- .../DynamicSettingsListenerTest.php | 165 ++++++++++++++++++ .../OriginalRequestListenerTest.php | 84 +++++++++ .../RequestEventListenerTest.php | 12 +- .../EzPublishIOBundle/Resources/config/io.yml | 4 +- .../EventListener/SiteAccessMatchListener.php | 42 +++-- .../Tests/SiteAccessMatchListenerTest.php | 51 +++++- .../Routing/Generator/UrlAliasGenerator.php | 5 + 16 files changed, 506 insertions(+), 122 deletions(-) create mode 100644 eZ/Bundle/EzPublishCoreBundle/EventListener/DynamicSettingsListener.php create mode 100644 eZ/Bundle/EzPublishCoreBundle/EventListener/OriginalRequestListener.php create mode 100644 eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/DynamicSettingsListenerTest.php create mode 100644 eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/OriginalRequestListenerTest.php diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/ConfigScopeListener.php b/eZ/Bundle/EzPublishCoreBundle/EventListener/ConfigScopeListener.php index b98fbb6ca6..a31dae9058 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/ConfigScopeListener.php +++ b/eZ/Bundle/EzPublishCoreBundle/EventListener/ConfigScopeListener.php @@ -14,11 +14,9 @@ use eZ\Publish\Core\MVC\Symfony\MVCEvents; use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; use eZ\Publish\Core\MVC\Symfony\View\ViewManagerInterface; -use Symfony\Component\DependencyInjection\ContainerAware; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -class ConfigScopeListener extends ContainerAware implements EventSubscriberInterface +class ConfigScopeListener implements EventSubscriberInterface { /** * @var \eZ\Publish\Core\MVC\Symfony\Configuration\VersatileScopeInterface @@ -30,38 +28,20 @@ class ConfigScopeListener extends ContainerAware implements EventSubscriberInter */ private $viewManager; - /** - * Array of serviceIds to reset in the container. - * - * @var array - */ - private $resettableServiceIds; - - /** - * Array of "fake" services handling dynamic settings injection. - * - * @var array - */ - private $dynamicSettingsServiceIds; - public function __construct( VersatileScopeInterface $configResolver, - ViewManagerInterface $viewManager, - array $resettableServiceIds, - array $dynamicSettingsServiceIds + ViewManagerInterface $viewManager ) { $this->configResolver = $configResolver; $this->viewManager = $viewManager; - $this->resettableServiceIds = $resettableServiceIds; - $this->dynamicSettingsServiceIds = $dynamicSettingsServiceIds; } public static function getSubscribedEvents() { return array( - MVCEvents::CONFIG_SCOPE_CHANGE => 'onConfigScopeChange', - MVCEvents::CONFIG_SCOPE_RESTORE => 'onConfigScopeChange', + MVCEvents::CONFIG_SCOPE_CHANGE => array( 'onConfigScopeChange', 100 ), + MVCEvents::CONFIG_SCOPE_RESTORE => array( 'onConfigScopeChange', 100 ) ); } @@ -73,19 +53,5 @@ public function onConfigScopeChange( ScopeChangeEvent $event ) { $this->viewManager->setSiteAccess( $siteAccess ); } - - // Ensure to reset services that need to be. - foreach ( $this->resettableServiceIds as $serviceId ) - { - $this->container->set( $serviceId, null ); - } - - // Force dynamic settings services to synchronize. - // This will trigger services depending on dynamic settings to update if they use setter injection. - foreach ( $this->dynamicSettingsServiceIds as $fakeServiceId ) - { - $this->container->set( $fakeServiceId, null ); - $this->container->set( $fakeServiceId, $this->container->get( $fakeServiceId ) ); - } } } diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/DynamicSettingsListener.php b/eZ/Bundle/EzPublishCoreBundle/EventListener/DynamicSettingsListener.php new file mode 100644 index 0000000000..f7096cd6f8 --- /dev/null +++ b/eZ/Bundle/EzPublishCoreBundle/EventListener/DynamicSettingsListener.php @@ -0,0 +1,85 @@ +resettableServiceIds = $resettableServiceIds; + $this->dynamicSettingsServiceIds = $dynamicSettingsServiceIds; + } + + public static function getSubscribedEvents() + { + return array( + MVCEvents::SITEACCESS => array( 'onSiteAccessMatch', 254 ), + MVCEvents::CONFIG_SCOPE_CHANGE => array( 'onConfigScopeChange', 90 ), + MVCEvents::CONFIG_SCOPE_RESTORE => array( 'onConfigScopeChange', 90 ) + ); + } + + public function onSiteAccessMatch( PostSiteAccessMatchEvent $event ) + { + if ( $event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST ) + { + return; + } + + $this->resetDynamicSettings(); + } + + public function onConfigScopeChange( ScopeChangeEvent $event ) + { + $this->resetDynamicSettings(); + } + + /** + * Ensure that dynamic settings are correctly reset, + * so that services that rely on those are correctly updated + */ + private function resetDynamicSettings() + { + // Ensure to reset services that need to be. + foreach ( $this->resettableServiceIds as $serviceId ) + { + $this->container->set( $serviceId, null ); + } + + // Force dynamic settings services to synchronize. + // This will trigger services depending on dynamic settings to update if they use setter injection. + foreach ( $this->dynamicSettingsServiceIds as $fakeServiceId ) + { + $this->container->set( $fakeServiceId, null ); + $this->container->set( $fakeServiceId, $this->container->get( $fakeServiceId ) ); + } + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/OriginalRequestListener.php b/eZ/Bundle/EzPublishCoreBundle/EventListener/OriginalRequestListener.php new file mode 100644 index 0000000000..c3e280beee --- /dev/null +++ b/eZ/Bundle/EzPublishCoreBundle/EventListener/OriginalRequestListener.php @@ -0,0 +1,53 @@ + array( 'onKernelRequest', 200 ) + ); + } + + public function onKernelRequest( GetResponseEvent $event ) + { + if ( $event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST ) + { + return; + } + + $request = $event->getRequest(); + if ( !$request->headers->has( 'x-fos-original-url' ) ) + { + return; + } + + $originalRequest = Request::create( + $request->getSchemeAndHttpHost() . $request->headers->get( 'x-fos-original-url' ), + 'GET', array(), array(), array(), + array( 'HTTP_ACCEPT' => $request->headers->get( 'x-fos-original-accept' ) ) + ); + $originalRequest->headers->set( 'user-agent', $request->headers->get( 'user-agent' ) ); + $originalRequest->headers->set( 'accept-language', $request->headers->get( 'accept-language' ) ); + $request->attributes->set( '_ez_original_request', $originalRequest ); + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/RequestEventListener.php b/eZ/Bundle/EzPublishCoreBundle/EventListener/RequestEventListener.php index 1f9b9048b6..68c2c390e4 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/RequestEventListener.php +++ b/eZ/Bundle/EzPublishCoreBundle/EventListener/RequestEventListener.php @@ -46,18 +46,12 @@ class RequestEventListener implements EventSubscriberInterface */ private $router; - /** - * @var \eZ\Publish\SPI\HashGenerator - */ - private $hashGenerator; - - public function __construct( ConfigResolverInterface $configResolver, RouterInterface $router, $defaultSiteAccess, HashGenerator $hashGenerator, LoggerInterface $logger = null ) + public function __construct( ConfigResolverInterface $configResolver, RouterInterface $router, $defaultSiteAccess, LoggerInterface $logger = null ) { $this->configResolver = $configResolver; $this->defaultSiteAccess = $defaultSiteAccess; $this->router = $router; $this->logger = $logger; - $this->hashGenerator = $hashGenerator; } public static function getSubscribedEvents() diff --git a/eZ/Bundle/EzPublishCoreBundle/HttpCache.php b/eZ/Bundle/EzPublishCoreBundle/HttpCache.php index 24800aea93..9d172036e0 100644 --- a/eZ/Bundle/EzPublishCoreBundle/HttpCache.php +++ b/eZ/Bundle/EzPublishCoreBundle/HttpCache.php @@ -114,4 +114,11 @@ protected function getInternalAllowedIPs() { return array( '127.0.0.1', '::1', 'fe80::1' ); } + + protected function cleanupForwardRequest( Request $forwardReq, Request $originalRequest ) + { + parent::cleanupForwardRequest( $forwardReq, $originalRequest ); + // Embed the original request as we need it to match the SiteAccess. + $forwardReq->attributes->set( '_ez_original_request', $originalRequest ); + } } diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/helpers.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/helpers.yml index 81571e6ae7..86d0df0eca 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/helpers.yml +++ b/eZ/Bundle/EzPublishCoreBundle/Resources/config/helpers.yml @@ -4,6 +4,7 @@ parameters: ezpublish.field_helper.class: eZ\Publish\Core\Helper\FieldHelper ezpublish.content_preview_helper.class: eZ\Publish\Core\Helper\ContentPreviewHelper ezpublish.config_scope_listener.class: eZ\Bundle\EzPublishCoreBundle\EventListener\ConfigScopeListener + ezpublish.dynamic_settings_listener.class: eZ\Bundle\EzPublishCoreBundle\EventListener\DynamicSettingsListener ezpublish.config_resolver.resettable_services: [] ezpublish.config_resolver.dynamic_settings_services: [] @@ -32,6 +33,12 @@ services: arguments: - @ezpublish.config.resolver.core - @ezpublish.view_manager + tags: + - { name: kernel.event_subscriber } + + ezpublish.dynamic_settings_listener: + class: %ezpublish.dynamic_settings_listener.class% + arguments: - %ezpublish.config_resolver.resettable_services% - %ezpublish.config_resolver.dynamic_settings_services% calls: diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing.yml index d06f9002db..8f54f0d084 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing.yml +++ b/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing.yml @@ -16,6 +16,7 @@ parameters: ezpublish.siteaccess_match_listener.class: eZ\Publish\Core\MVC\Symfony\EventListener\SiteAccessMatchListener ezpublish.route_reference.generator.class: eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator ezpublish.route_reference.listener.language_switch.class: eZ\Publish\Core\MVC\Symfony\EventListener\LanguageSwitchListener + ezpublish.original_request_listener.class: eZ\Bundle\EzPublishCoreBundle\EventListener\OriginalRequestListener services: ezpublish.chain_router: @@ -92,7 +93,6 @@ services: - @ezpublish.config.resolver - @router - %ezpublish.siteaccess.default% - - @ezpublish.user.hash_generator - @?logger tags: - { name: kernel.event_subscriber } @@ -108,3 +108,8 @@ services: arguments: [@ezpublish.translation_helper] tags: - { name: kernel.event_subscriber } + + ezpublish.original_request_listener: + class: %ezpublish.original_request_listener.class% + tags: + - { name: kernel.event_subscriber } diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing/internal.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing/internal.yml index f17cfe9ace..554adea052 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing/internal.yml +++ b/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing/internal.yml @@ -13,3 +13,6 @@ _ezpublishPreviewContent: _ezpublishPreviewContentDefaultSa: path: /content/versionview/{contentId}/{versionNo}/{language} defaults: { _controller: ezpublish.controller.content.preview:previewContentAction } + +_ez_user_hash: + path: /_fos_user_context_hash diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ConfigScopeListenerTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ConfigScopeListenerTest.php index bffd13a3b8..e720adbd74 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ConfigScopeListenerTest.php +++ b/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ConfigScopeListenerTest.php @@ -27,25 +27,19 @@ class ConfigScopeListenerTest extends PHPUnit_Framework_TestCase */ private $viewManager; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $container; - protected function setUp() { parent::setUp(); $this->configResolver = $this->getMock( 'eZ\Publish\Core\MVC\Symfony\Configuration\VersatileScopeInterface' ); $this->viewManager = $this->getMock( 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\Stubs\ViewManager' ); - $this->container = $this->getMock( 'Symfony\Component\DependencyInjection\ContainerInterface' ); } public function testGetSubscribedEvents() { $this->assertSame( array( - MVCEvents::CONFIG_SCOPE_CHANGE => 'onConfigScopeChange', - MVCEvents::CONFIG_SCOPE_RESTORE => 'onConfigScopeChange', + MVCEvents::CONFIG_SCOPE_CHANGE => array( 'onConfigScopeChange', 100 ), + MVCEvents::CONFIG_SCOPE_RESTORE => array( 'onConfigScopeChange', 100 ) ), ConfigScopeListener::getSubscribedEvents() ); @@ -55,8 +49,6 @@ public function testOnConfigScopeChange() { $siteAccess = new SiteAccess( 'test' ); $event = new ScopeChangeEvent( $siteAccess ); - $resettableServices = array( 'foo', 'bar.baz' ); - $dynamicSettingsServiceIds = array( 'something', 'something_else' ); $this->configResolver ->expects( $this->once() ) ->method( 'setDefaultScope' ) @@ -65,47 +57,8 @@ public function testOnConfigScopeChange() ->expects( $this->once() ) ->method( 'setSiteAccess' ) ->with( $siteAccess ); - $this->container - ->expects( $this->at( 0 ) ) - ->method( 'set' ) - ->with( 'foo', null ); - $this->container - ->expects( $this->at( 1 ) ) - ->method( 'set' ) - ->with( 'bar.baz', null ); - - $fakeService1 = new \stdClass; - $this->container - ->expects( $this->at( 2 ) ) - ->method( 'set' ) - ->with( 'something', null ); - $this->container - ->expects( $this->at( 3 ) ) - ->method( 'get' ) - ->with( 'something' ) - ->will( $this->returnValue( $fakeService1 ) ); - $this->container - ->expects( $this->at( 4 ) ) - ->method( 'set' ) - ->with( 'something', $fakeService1 ); - - $fakeService2 = new \stdClass; - $this->container - ->expects( $this->at( 5 ) ) - ->method( 'set' ) - ->with( 'something_else', null ); - $this->container - ->expects( $this->at( 6 ) ) - ->method( 'get' ) - ->with( 'something_else' ) - ->will( $this->returnValue( $fakeService2 ) ); - $this->container - ->expects( $this->at( 7 ) ) - ->method( 'set' ) - ->with( 'something_else', $fakeService2 ); - $listener = new ConfigScopeListener( $this->configResolver, $this->viewManager, $resettableServices, $dynamicSettingsServiceIds ); - $listener->setContainer( $this->container ); + $listener = new ConfigScopeListener( $this->configResolver, $this->viewManager ); $listener->onConfigScopeChange( $event ); $this->assertSame( $siteAccess, $event->getSiteAccess() ); } diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/DynamicSettingsListenerTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/DynamicSettingsListenerTest.php new file mode 100644 index 0000000000..56c760d152 --- /dev/null +++ b/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/DynamicSettingsListenerTest.php @@ -0,0 +1,165 @@ +container = $this->getMock( 'Symfony\Component\DependencyInjection\ContainerInterface' ); + } + + public function testGetSubscribedEvents() + { + $this->assertSame( + array( + MVCEvents::SITEACCESS => array( 'onSiteAccessMatch', 254 ), + MVCEvents::CONFIG_SCOPE_CHANGE => array( 'onConfigScopeChange', 90 ), + MVCEvents::CONFIG_SCOPE_RESTORE => array( 'onConfigScopeChange', 90 ) + ), + DynamicSettingsListener::getSubscribedEvents() + ); + } + + public function testOnSiteAccessMatchSubRequest() + { + $event = new PostSiteAccessMatchEvent( new SiteAccess( 'test' ), new Request(), HttpKernelInterface::SUB_REQUEST ); + $resettableServices = array( 'foo', 'bar.baz' ); + $dynamicSettingsServiceIds = array( 'something', 'something_else' ); + + $this->container + ->expects( $this->never() ) + ->method( 'set' ); + $this->container + ->expects( $this->never() ) + ->method( 'get' ); + + $listener = new DynamicSettingsListener( $resettableServices, $dynamicSettingsServiceIds ); + $listener->setContainer( $this->container ); + $listener->onSiteAccessMatch( $event ); + } + + public function testOnSiteAccessMatch() + { + $event = new PostSiteAccessMatchEvent( new SiteAccess( 'test' ), new Request(), HttpKernelInterface::MASTER_REQUEST ); + $resettableServices = array( 'foo', 'bar.baz' ); + $dynamicSettingsServiceIds = array( 'something', 'something_else' ); + + $this->container + ->expects( $this->at( 0 ) ) + ->method( 'set' ) + ->with( 'foo', null ); + $this->container + ->expects( $this->at( 1 ) ) + ->method( 'set' ) + ->with( 'bar.baz', null ); + + $fakeService1 = new \stdClass; + $this->container + ->expects( $this->at( 2 ) ) + ->method( 'set' ) + ->with( 'something', null ); + $this->container + ->expects( $this->at( 3 ) ) + ->method( 'get' ) + ->with( 'something' ) + ->will( $this->returnValue( $fakeService1 ) ); + $this->container + ->expects( $this->at( 4 ) ) + ->method( 'set' ) + ->with( 'something', $fakeService1 ); + + $fakeService2 = new \stdClass; + $this->container + ->expects( $this->at( 5 ) ) + ->method( 'set' ) + ->with( 'something_else', null ); + $this->container + ->expects( $this->at( 6 ) ) + ->method( 'get' ) + ->with( 'something_else' ) + ->will( $this->returnValue( $fakeService2 ) ); + $this->container + ->expects( $this->at( 7 ) ) + ->method( 'set' ) + ->with( 'something_else', $fakeService2 ); + + $listener = new DynamicSettingsListener( $resettableServices, $dynamicSettingsServiceIds ); + $listener->setContainer( $this->container ); + $listener->onSiteAccessMatch( $event ); + } + + public function testOnConfigScopeChange() + { + $siteAccess = new SiteAccess( 'test' ); + $event = new ScopeChangeEvent( $siteAccess ); + $resettableServices = array( 'foo', 'bar.baz' ); + $dynamicSettingsServiceIds = array( 'something', 'something_else' ); + + $this->container + ->expects( $this->at( 0 ) ) + ->method( 'set' ) + ->with( 'foo', null ); + $this->container + ->expects( $this->at( 1 ) ) + ->method( 'set' ) + ->with( 'bar.baz', null ); + + $fakeService1 = new \stdClass; + $this->container + ->expects( $this->at( 2 ) ) + ->method( 'set' ) + ->with( 'something', null ); + $this->container + ->expects( $this->at( 3 ) ) + ->method( 'get' ) + ->with( 'something' ) + ->will( $this->returnValue( $fakeService1 ) ); + $this->container + ->expects( $this->at( 4 ) ) + ->method( 'set' ) + ->with( 'something', $fakeService1 ); + + $fakeService2 = new \stdClass; + $this->container + ->expects( $this->at( 5 ) ) + ->method( 'set' ) + ->with( 'something_else', null ); + $this->container + ->expects( $this->at( 6 ) ) + ->method( 'get' ) + ->with( 'something_else' ) + ->will( $this->returnValue( $fakeService2 ) ); + $this->container + ->expects( $this->at( 7 ) ) + ->method( 'set' ) + ->with( 'something_else', $fakeService2 ); + + $listener = new DynamicSettingsListener( $resettableServices, $dynamicSettingsServiceIds ); + $listener->setContainer( $this->container ); + $listener->onConfigScopeChange( $event ); + $this->assertSame( $siteAccess, $event->getSiteAccess() ); + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/OriginalRequestListenerTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/OriginalRequestListenerTest.php new file mode 100644 index 0000000000..07f6752218 --- /dev/null +++ b/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/OriginalRequestListenerTest.php @@ -0,0 +1,84 @@ +assertSame( + array( + KernelEvents::REQUEST => array( 'onKernelRequest', 200 ) + ), + OriginalRequestListener::getSubscribedEvents() + ); + } + + public function testOnKernelRequestNotMaster() + { + $request = new Request(); + $event = new GetResponseEvent( + $this->getMock( '\Symfony\Component\HttpKernel\HttpKernelInterface' ), + $request, + HttpKernelInterface::SUB_REQUEST + ); + + $listener = new OriginalRequestListener(); + $listener->onKernelRequest( $event ); + $this->assertFalse( $request->attributes->has( '_ez_original_request' ) ); + } + + public function testOnKernelRequestNoOriginalRequest() + { + $request = new Request(); + $event = new GetResponseEvent( + $this->getMock( '\Symfony\Component\HttpKernel\HttpKernelInterface' ), + $request, + HttpKernelInterface::MASTER_REQUEST + ); + + $listener = new OriginalRequestListener(); + $listener->onKernelRequest( $event ); + $this->assertFalse( $request->attributes->has( '_ez_original_request' ) ); + } + + public function testOnKernelRequestWithOriginalRequest() + { + $scheme = 'http'; + $host = 'phoenix-rises.fm'; + $port = 1234; + $originalUri = '/foo/bar'; + $originalAccept = 'blabla'; + + $expectedOriginalRequest = Request::create( sprintf( '%s://%s:%d%s', $scheme, $host, $port, $originalUri ) ); + $expectedOriginalRequest->headers->set( 'accept', $originalAccept ); + $expectedOriginalRequest->server->set( 'HTTP_ACCEPT', $originalAccept ); + + $request = Request::create( sprintf( '%s://%s:%d', $scheme, $host, $port ) . '/_fos_user_hash' ); + $request->headers->set( 'x-fos-original-url', $originalUri ); + $request->headers->set( 'x-fos-original-accept', $originalAccept ); + $event = new GetResponseEvent( + $this->getMock( '\Symfony\Component\HttpKernel\HttpKernelInterface' ), + $request, + HttpKernelInterface::MASTER_REQUEST + ); + + $listener = new OriginalRequestListener(); + $listener->onKernelRequest( $event ); + $this->assertEquals( $expectedOriginalRequest, $request->attributes->get( '_ez_original_request' ) ); + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RequestEventListenerTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RequestEventListenerTest.php index 92c0d954c5..0be3a54d4d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RequestEventListenerTest.php +++ b/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RequestEventListenerTest.php @@ -33,11 +33,6 @@ class RequestEventListenerTest extends \PHPUnit_Framework_TestCase */ private $router; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $hashGenerator; - /** * @var \PHPUnit_Framework_MockObject_MockObject|LoggerInterface */ @@ -69,10 +64,9 @@ protected function setUp() $this->configResolver = $this->getMock( 'eZ\Publish\Core\MVC\ConfigResolverInterface' ); $this->router = $this->getMock( 'Symfony\Component\Routing\RouterInterface' ); - $this->hashGenerator = $this->getMock( 'eZ\\Publish\\SPI\\HashGenerator' ); $this->logger = $this->getMock( 'Psr\\Log\\LoggerInterface' ); - $this->requestEventListener = new RequestEventListener( $this->configResolver, $this->router, 'foobar', $this->hashGenerator, $this->logger ); + $this->requestEventListener = new RequestEventListener( $this->configResolver, $this->router, 'foobar', $this->logger ); $this->request = $this ->getMockBuilder( 'Symfony\\Component\\HttpFoundation\\Request' ) @@ -208,7 +202,7 @@ public function testOnKernelRequestSetupAlreadySetupUri() ->method( 'getContext' ) ->will( $this->returnValue( $this->getMock( 'Symfony\Component\Routing\RequestContext' ) ) ); - $requestEventListener = new RequestEventListener( $this->configResolver, $this->router, 'setup', $this->hashGenerator, $this->logger ); + $requestEventListener = new RequestEventListener( $this->configResolver, $this->router, 'setup', $this->logger ); $event = new GetResponseEvent( $this->httpKernel, Request::create( '/setup' ), HttpKernelInterface::MASTER_REQUEST ); $requestEventListener->onKernelRequestSetup( $event ); $this->assertFalse( $event->hasResponse() ); @@ -226,7 +220,7 @@ public function testOnKernelRequestSetup() ->method( 'getContext' ) ->will( $this->returnValue( $this->getMock( 'Symfony\Component\Routing\RequestContext' ) ) ); - $requestEventListener = new RequestEventListener( $this->configResolver, $this->router, 'setup', $this->hashGenerator, $this->logger ); + $requestEventListener = new RequestEventListener( $this->configResolver, $this->router, 'setup', $this->logger ); $event = new GetResponseEvent( $this->httpKernel, Request::create( '/foo/bar' ), HttpKernelInterface::MASTER_REQUEST ); $requestEventListener->onKernelRequestSetup( $event ); $this->assertTrue( $event->hasResponse() ); diff --git a/eZ/Bundle/EzPublishIOBundle/Resources/config/io.yml b/eZ/Bundle/EzPublishIOBundle/Resources/config/io.yml index 38cf2d8187..75a4d381ac 100644 --- a/eZ/Bundle/EzPublishIOBundle/Resources/config/io.yml +++ b/eZ/Bundle/EzPublishIOBundle/Resources/config/io.yml @@ -69,8 +69,8 @@ services: ezpublish.core.io.image_fieldtype.legacy_url_decorator: class: %ezpublish.core.io.url_decorator.prefix.class% - arguments: - - "$io.legacy_url_prefix$" + calls: + - [setPrefix, ["$io.legacy_url_prefix$"]] ezpublish.core.io.stream_file_listener: class: %ezpublish.core.io.stream_file_listener.class% diff --git a/eZ/Publish/Core/MVC/Symfony/EventListener/SiteAccessMatchListener.php b/eZ/Publish/Core/MVC/Symfony/EventListener/SiteAccessMatchListener.php index 6209a7ac49..e1ea235dbf 100644 --- a/eZ/Publish/Core/MVC/Symfony/EventListener/SiteAccessMatchListener.php +++ b/eZ/Publish/Core/MVC/Symfony/EventListener/SiteAccessMatchListener.php @@ -10,6 +10,7 @@ namespace eZ\Publish\Core\MVC\Symfony\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestMatcherInterface; use Symfony\Component\HttpKernel\KernelEvents; use eZ\Publish\Core\MVC\Symfony\SiteAccess; @@ -68,8 +69,9 @@ public static function getSubscribedEvents() public function onKernelRequest( GetResponseEvent $event ) { $request = $event->getRequest(); + // Don't try to match when it's a user hash request. SiteAccess is irrelevant in this case. - if ( $this->userContextRequestMatcher->matches( $request ) ) + if ( $this->userContextRequestMatcher->matches( $request ) && !$request->attributes->has( '_ez_original_request' ) ) { return; } @@ -85,21 +87,11 @@ public function onKernelRequest( GetResponseEvent $event ) } else if ( !$request->attributes->has( 'siteaccess' ) ) { + // Get SiteAccess from original request if present ("_ez_original_request" attribute), or current request otherwise. + // "_ez_original_request" attribute is present in the case of user context hash generation (aka "user hash request"). $request->attributes->set( 'siteaccess', - $this->siteAccessRouter->match( - new SimplifiedRequest( - array( - 'scheme' => $request->getScheme(), - 'host' => $request->getHost(), - 'port' => $request->getPort(), - 'pathinfo' => $request->getPathInfo(), - 'queryParams' => $request->query->all(), - 'languages' => $request->getLanguages(), - 'headers' => $request->headers->all() - ) - ) - ) + $this->getSiteAccessFromRequest( $request->attributes->get( '_ez_original_request', $request ) ) ); } @@ -110,4 +102,26 @@ public function onKernelRequest( GetResponseEvent $event ) $this->eventDispatcher->dispatch( MVCEvents::SITEACCESS, $siteAccessEvent ); } } + + /** + * @param Request $request + * + * @return SiteAccess + */ + private function getSiteAccessFromRequest( Request $request ) + { + return $this->siteAccessRouter->match( + new SimplifiedRequest( + array( + 'scheme' => $request->getScheme(), + 'host' => $request->getHost(), + 'port' => $request->getPort(), + 'pathinfo' => $request->getPathInfo(), + 'queryParams' => $request->query->all(), + 'languages' => $request->getLanguages(), + 'headers' => $request->headers->all() + ) + ) + ); + } } diff --git a/eZ/Publish/Core/MVC/Symfony/EventListener/Tests/SiteAccessMatchListenerTest.php b/eZ/Publish/Core/MVC/Symfony/EventListener/Tests/SiteAccessMatchListenerTest.php index 4e4b16c80d..d319708d51 100644 --- a/eZ/Publish/Core/MVC/Symfony/EventListener/Tests/SiteAccessMatchListenerTest.php +++ b/eZ/Publish/Core/MVC/Symfony/EventListener/Tests/SiteAccessMatchListenerTest.php @@ -61,7 +61,7 @@ public function testGetSubscribedEvents() ); } - public function testOnKernelRequestUserHash() + public function testOnKernelRequestUserHashNoOriginalRequest() { $request = new Request(); $event = new GetResponseEvent( @@ -196,4 +196,53 @@ public function testOnKernelRequest() $this->listener->onKernelRequest( $event ); $this->assertSame( $siteAccess, $request->attributes->get( 'siteaccess' ) ); } + + public function testOnKernelRequestUserHashWithOriginalRequest() + { + $siteAccess = new SiteAccess(); + $scheme = 'https'; + $host = 'phoenix-rises.fm'; + $port = 1234; + $path = '/foo/bar'; + $originalRequest = Request::create( sprintf( '%s://%s:%d%s', $scheme, $host, $port, $path ) ); + $request = Request::create( 'http://localhost/_fos_user_hash' ); + $request->attributes->set( '_ez_original_request', $originalRequest ); + $event = new GetResponseEvent( + $this->getMock( '\Symfony\Component\HttpKernel\HttpKernelInterface' ), + $request, + HttpKernelInterface::MASTER_REQUEST + ); + + $this->userHashMatcher + ->expects( $this->once() ) + ->method( 'matches' ) + ->with( $request ) + ->will( $this->returnValue( true ) ); + + $simplifiedRequest = new SimplifiedRequest( + array( + 'scheme' => $originalRequest->getScheme(), + 'host' => $originalRequest->getHost(), + 'port' => $originalRequest->getPort(), + 'pathinfo' => $originalRequest->getPathInfo(), + 'queryParams' => $originalRequest->query->all(), + 'languages' => $originalRequest->getLanguages(), + 'headers' => $originalRequest->headers->all() + ) + ); + $this->saRouter + ->expects( $this->once() ) + ->method( 'match' ) + ->with( $this->equalTo( $simplifiedRequest ) ) + ->will( $this->returnValue( $siteAccess ) ); + + $postSAMatchEvent = new PostSiteAccessMatchEvent( $siteAccess, $request, $event->getRequestType() ); + $this->eventDispatcher + ->expects( $this->once() ) + ->method( 'dispatch' ) + ->with( MVCEvents::SITEACCESS, $this->equalTo( $postSAMatchEvent ) ); + + $this->listener->onKernelRequest( $event ); + $this->assertSame( $siteAccess, $request->attributes->get( 'siteaccess' ) ); + } } diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php b/eZ/Publish/Core/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php index 4d0a4af5c3..0ae34471a0 100644 --- a/eZ/Publish/Core/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php +++ b/eZ/Publish/Core/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php @@ -158,6 +158,11 @@ public function setExcludedUriPrefixes( array $excludedUriPrefixes ) */ public function getPathPrefixByRootLocationId( $rootLocationId ) { + if ( !$rootLocationId ) + { + return ''; + } + if ( isset( $this->pathPrefixMap[$rootLocationId] ) ) { return $this->pathPrefixMap[$rootLocationId];