From 992cdd09f91b63159e6e06445cca25ff693a1e69 Mon Sep 17 00:00:00 2001 From: morgan Date: Fri, 29 Mar 2024 15:59:42 +0100 Subject: [PATCH 1/7] Implementation of a bypass for public actions --- classes/controller/AdminController.php | 2 +- src/Adapter/Security/Admin.php | 5 +++-- src/Core/Context/LegacyControllerContextBuilder.php | 4 ++++ .../Controller/Admin/LegacyController.php | 12 +++++------- .../EventListener/Admin/TokenizedUrlsListener.php | 4 +++- src/PrestaShopBundle/Routing/LegacyRouterChecker.php | 8 ++++++++ 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/classes/controller/AdminController.php b/classes/controller/AdminController.php index 182532efdb9da..d31a1f9706856 100644 --- a/classes/controller/AdminController.php +++ b/classes/controller/AdminController.php @@ -4395,7 +4395,7 @@ protected function setAllowAnonymous($value) * * @return bool */ - protected function isAnonymousAllowed() + public function isAnonymousAllowed() { return $this->allowAnonymous; } diff --git a/src/Adapter/Security/Admin.php b/src/Adapter/Security/Admin.php index 70c40deab6041..9173305f86044 100644 --- a/src/Adapter/Security/Admin.php +++ b/src/Adapter/Security/Admin.php @@ -27,8 +27,8 @@ namespace PrestaShop\PrestaShop\Adapter\Security; use PrestaShop\PrestaShop\Adapter\LegacyContext; +use PrestaShopBundle\Routing\LegacyRouterChecker; use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -88,7 +88,8 @@ public function __construct( */ public function onKernelRequest(RequestEvent $event): void { - if ($this->security->getUser() !== null) { + $publicLegacyRoute = $event->getRequest()->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_ANONYMOUS_ATTRIBUTE); + if ($this->security->getUser() !== null || $publicLegacyRoute) { return; } diff --git a/src/Core/Context/LegacyControllerContextBuilder.php b/src/Core/Context/LegacyControllerContextBuilder.php index 5e96c00d3d218..8ba392adc8407 100644 --- a/src/Core/Context/LegacyControllerContextBuilder.php +++ b/src/Core/Context/LegacyControllerContextBuilder.php @@ -88,6 +88,10 @@ public function build(): LegacyControllerContext public function buildLegacyContext(): void { + if ($this->contextStateManager->getContext()->controller) { + return; + } + $this->assertArguments(); if (null === $this->_legacyControllerContext) { diff --git a/src/PrestaShopBundle/Controller/Admin/LegacyController.php b/src/PrestaShopBundle/Controller/Admin/LegacyController.php index 6c2e02881310c..3d85fe7911c35 100644 --- a/src/PrestaShopBundle/Controller/Admin/LegacyController.php +++ b/src/PrestaShopBundle/Controller/Admin/LegacyController.php @@ -86,7 +86,7 @@ public function legacyPageAction(Request $request): Response 'is_module' => $request->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_IS_MODULE_ATTRIBUTE), ]; - $adminController = $this->initController($dispatcherHookParameters); + $adminController = $this->initController($request, $dispatcherHookParameters); // Redirect if necessary after post process if (!empty($adminController->getRedirectAfter())) { // After each request the cookie must be written to save its modified state during AdminController workflow @@ -226,17 +226,16 @@ protected function renderAjaxController(AdminController $adminController, string * * Note: some legacy controllers may already use die at this point (to echo content and finish the process) when postProcess is called. * + * @param Request $request * @param array $dispatcherHookParameters * * @return AdminController */ - protected function initController(array $dispatcherHookParameters): AdminController + protected function initController(Request $request, array $dispatcherHookParameters): AdminController { - $controllerClass = $dispatcherHookParameters['controller_class']; - - // Loading controller + // Retrieving the controller instantiated in LegacyRouterChecker /** @var AdminController $adminController */ - $adminController = new $controllerClass(); + $adminController = $request->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_INSTANCE_ATTRIBUTE); // Fill default smarty variables as they can be used in partial templates rendered in init methods $this->assignSmartyVariables->fillDefault(); @@ -246,7 +245,6 @@ protected function initController(array $dispatcherHookParameters): AdminControl // This part comes from AdminController::run method, it has been stripped from permission checks since the permission is already // handled by this Symfony controller - $adminController->init(); $adminController->setMedia(false); $adminController->postProcess(); diff --git a/src/PrestaShopBundle/EventListener/Admin/TokenizedUrlsListener.php b/src/PrestaShopBundle/EventListener/Admin/TokenizedUrlsListener.php index b82b54bbd39db..e4b5293ecbaf9 100644 --- a/src/PrestaShopBundle/EventListener/Admin/TokenizedUrlsListener.php +++ b/src/PrestaShopBundle/EventListener/Admin/TokenizedUrlsListener.php @@ -28,6 +28,7 @@ use PrestaShop\PrestaShop\Core\Feature\TokenInUrls; use PrestaShop\PrestaShop\Core\Util\Url\UrlCleaner; +use PrestaShopBundle\Routing\LegacyRouterChecker; use PrestaShopBundle\Security\Admin\UserTokenManager; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpKernel\Event\KernelEvent; @@ -50,8 +51,9 @@ public function __construct( public function onKernelRequest(KernelEvent $event) { $request = $event->getRequest(); + $publicLegacyRoute = $event->getRequest()->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_ANONYMOUS_ATTRIBUTE); - if (TokenInUrls::isDisabled()) { + if (TokenInUrls::isDisabled() || $publicLegacyRoute) { return; } diff --git a/src/PrestaShopBundle/Routing/LegacyRouterChecker.php b/src/PrestaShopBundle/Routing/LegacyRouterChecker.php index dec5f4e5f2060..529e337d82e08 100644 --- a/src/PrestaShopBundle/Routing/LegacyRouterChecker.php +++ b/src/PrestaShopBundle/Routing/LegacyRouterChecker.php @@ -45,6 +45,8 @@ class LegacyRouterChecker { public const LEGACY_CONTROLLER_CLASS_ATTRIBUTE = '_legacy_controller_class'; + public const LEGACY_CONTROLLER_INSTANCE_ATTRIBUTE = '_legacy_controller_instance_class'; + public const LEGACY_CONTROLLER_ANONYMOUS_ATTRIBUTE = '_legacy_controller_anonymous_class'; public const LEGACY_CONTROLLER_IS_MODULE_ATTRIBUTE = '_legacy_controller_is_module'; public function __construct( @@ -116,6 +118,12 @@ public function check(Request $request): bool $controllerClass = $controllers[strtolower($queryController)]; } + // Loading controller + $adminController = new $controllerClass(); + $adminController->init(); + + $request->attributes->set(self::LEGACY_CONTROLLER_INSTANCE_ATTRIBUTE, $adminController); + $request->attributes->set(self::LEGACY_CONTROLLER_ANONYMOUS_ATTRIBUTE, $adminController->isAnonymousAllowed()); $request->attributes->set(self::LEGACY_CONTROLLER_CLASS_ATTRIBUTE, $controllerClass); $request->attributes->set(self::LEGACY_CONTROLLER_IS_MODULE_ATTRIBUTE, $isModule); From 210f7db5f1981a0c301e9e2def896679f2125224 Mon Sep 17 00:00:00 2001 From: morgan Date: Wed, 3 Apr 2024 17:34:43 +0200 Subject: [PATCH 2/7] Remove unnecessary conditions for ajax actions --- .../Controller/Admin/LegacyController.php | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/PrestaShopBundle/Controller/Admin/LegacyController.php b/src/PrestaShopBundle/Controller/Admin/LegacyController.php index 3d85fe7911c35..85840cd2b41c3 100644 --- a/src/PrestaShopBundle/Controller/Admin/LegacyController.php +++ b/src/PrestaShopBundle/Controller/Admin/LegacyController.php @@ -194,30 +194,8 @@ protected function renderAjaxController(AdminController $adminController, string } elseif (method_exists($adminController, 'displayAjax')) { $adminController->displayAjax(); } - $outputContent = ob_get_clean(); - - // The output of the controller is either directly echoed or it can be properly appended in AdminController::content - // it depends on the implementation of the controllers. - if (!empty($outputContent) && !empty($adminController->content)) { - // Sometimes they are both done and are equal, in which case we only return one content to avoid duplicates. - if ($outputContent === $adminController->content) { - $responseContent = $outputContent; - } else { - // In case both contents are present and are different we concatenate them, first the echoed output and then the controller - // one since it is displayed last by smarty in the original workflow - // This concatenation approach is theoretical and naive and was not tested because of the lack of use cases, so it may need - // to be improved in the future - $responseContent = $outputContent . $adminController->content; - } - } elseif (!empty($outputContent)) { - $responseContent = $outputContent; - } elseif (!empty($adminController->content)) { - $responseContent = $adminController->content; - } else { - $responseContent = ''; - } - return new Response($responseContent); + return new Response(ob_get_clean()); } /** From 5a127d7c1c00c70b039805356eb4733642b869e1 Mon Sep 17 00:00:00 2001 From: morgan Date: Fri, 5 Apr 2024 16:00:17 +0200 Subject: [PATCH 3/7] Fix shop context builder for legacy --- .../EventListener/Admin/Context/ShopContextListener.php | 8 ++++++++ src/PrestaShopBundle/Routing/LegacyRouterChecker.php | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php b/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php index 9d28c7b360405..b7738e1a1c8d4 100644 --- a/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php +++ b/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php @@ -37,6 +37,7 @@ use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint; use PrestaShop\PrestaShop\Core\Util\Url\UrlCleaner; use PrestaShopBundle\Controller\Attribute\AllShopContext; +use PrestaShopBundle\Routing\LegacyRouterChecker; use ReflectionClass; use ReflectionException; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -44,6 +45,7 @@ use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Routing\Exception\NoConfigurationException; use Symfony\Component\Routing\RouterInterface; +use PrestaShopBundle\Routing\LegacyRouterChecker; /** * Listener dedicated to set up Shop context for the Back-Office/Admin application. @@ -115,6 +117,12 @@ private function getMultiShopConstraint(Request $request): ShopConstraint return $shopConstraint; } + $isAllShopContext = $request->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_IS_ALL_SHOP_CONTEXT); + + if ($isAllShopContext) { + return $shopConstraint; + } + $shopConstraint = ShopConstraint::allShops(); $cookieShopConstraint = $this->getShopConstraintFromCookie(); if ($cookieShopConstraint) { diff --git a/src/PrestaShopBundle/Routing/LegacyRouterChecker.php b/src/PrestaShopBundle/Routing/LegacyRouterChecker.php index 529e337d82e08..8f5fcd0053810 100644 --- a/src/PrestaShopBundle/Routing/LegacyRouterChecker.php +++ b/src/PrestaShopBundle/Routing/LegacyRouterChecker.php @@ -29,6 +29,7 @@ namespace PrestaShopBundle\Routing; use Dispatcher; +use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint; use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; use PrestaShop\PrestaShop\Core\Hook\HookDispatcherInterface; @@ -48,6 +49,7 @@ class LegacyRouterChecker public const LEGACY_CONTROLLER_INSTANCE_ATTRIBUTE = '_legacy_controller_instance_class'; public const LEGACY_CONTROLLER_ANONYMOUS_ATTRIBUTE = '_legacy_controller_anonymous_class'; public const LEGACY_CONTROLLER_IS_MODULE_ATTRIBUTE = '_legacy_controller_is_module'; + public const LEGACY_CONTROLLER_IS_ALL_SHOP_CONTEXT = '_legacy_controller_is_all_shop_context'; public function __construct( protected readonly FeatureFlagStateCheckerInterface $featureFlagStateChecker, @@ -124,6 +126,7 @@ public function check(Request $request): bool $request->attributes->set(self::LEGACY_CONTROLLER_INSTANCE_ATTRIBUTE, $adminController); $request->attributes->set(self::LEGACY_CONTROLLER_ANONYMOUS_ATTRIBUTE, $adminController->isAnonymousAllowed()); + $request->attributes->set(self::LEGACY_CONTROLLER_IS_ALL_SHOP_CONTEXT, $adminController->multishop_context === ShopConstraint::ALL_SHOPS); $request->attributes->set(self::LEGACY_CONTROLLER_CLASS_ATTRIBUTE, $controllerClass); $request->attributes->set(self::LEGACY_CONTROLLER_IS_MODULE_ATTRIBUTE, $isModule); From 013cfa7c9c704357cd677e945f901713b77384e1 Mon Sep 17 00:00:00 2001 From: morgan Date: Tue, 9 Apr 2024 15:58:58 +0200 Subject: [PATCH 4/7] Introduce LegacyControllerConstants and add some comments --- src/Adapter/Security/Admin.php | 4 ++-- .../LegacyControllerContextBuilder.php | 2 ++ .../Controller/Admin/LegacyController.php | 8 +++---- .../Admin/Context/ShopContextListener.php | 8 +++---- .../Admin/TokenizedUrlsListener.php | 4 ++-- .../Routing/LegacyControllerConstants.php | 14 ++++++++++++ .../Routing/LegacyRouterChecker.php | 22 +++++++++---------- 7 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 src/PrestaShopBundle/Routing/LegacyControllerConstants.php diff --git a/src/Adapter/Security/Admin.php b/src/Adapter/Security/Admin.php index 9173305f86044..882fff59e0df8 100644 --- a/src/Adapter/Security/Admin.php +++ b/src/Adapter/Security/Admin.php @@ -27,7 +27,7 @@ namespace PrestaShop\PrestaShop\Adapter\Security; use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShopBundle\Routing\LegacyRouterChecker; +use PrestaShopBundle\Routing\LegacyControllerConstants; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -88,7 +88,7 @@ public function __construct( */ public function onKernelRequest(RequestEvent $event): void { - $publicLegacyRoute = $event->getRequest()->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_ANONYMOUS_ATTRIBUTE); + $publicLegacyRoute = $event->getRequest()->attributes->get(LegacyControllerConstants::ANONYMOUS_ATTRIBUTE); if ($this->security->getUser() !== null || $publicLegacyRoute) { return; } diff --git a/src/Core/Context/LegacyControllerContextBuilder.php b/src/Core/Context/LegacyControllerContextBuilder.php index 8ba392adc8407..7a883b4b49764 100644 --- a/src/Core/Context/LegacyControllerContextBuilder.php +++ b/src/Core/Context/LegacyControllerContextBuilder.php @@ -88,6 +88,8 @@ public function build(): LegacyControllerContext public function buildLegacyContext(): void { + // In legacy pages the AdminController class already sets the context's controller, which is a more accurate + // candidate than our facade meant for backward compatibility, so we leave it untouched if ($this->contextStateManager->getContext()->controller) { return; } diff --git a/src/PrestaShopBundle/Controller/Admin/LegacyController.php b/src/PrestaShopBundle/Controller/Admin/LegacyController.php index 85840cd2b41c3..f4b03c59c160c 100644 --- a/src/PrestaShopBundle/Controller/Admin/LegacyController.php +++ b/src/PrestaShopBundle/Controller/Admin/LegacyController.php @@ -32,7 +32,7 @@ use PrestaShop\PrestaShop\Core\ConfigurationInterface; use PrestaShop\PrestaShop\Core\Exception\CoreException; use PrestaShopBundle\Entity\Repository\TabRepository; -use PrestaShopBundle\Routing\LegacyRouterChecker; +use PrestaShopBundle\Routing\LegacyControllerConstants; use PrestaShopBundle\Twig\Layout\MenuBuilder; use PrestaShopBundle\Twig\Layout\SmartyVariablesFiller; use Symfony\Component\HttpFoundation\Request; @@ -82,8 +82,8 @@ public function legacyPageAction(Request $request): Response // These parameters have already been set as request attributes by LegacyRouterChecker $dispatcherHookParameters = [ 'controller_type' => Dispatcher::FC_ADMIN, - 'controller_class' => $request->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_CLASS_ATTRIBUTE), - 'is_module' => $request->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_IS_MODULE_ATTRIBUTE), + 'controller_class' => $request->attributes->get(LegacyControllerConstants::CLASS_ATTRIBUTE), + 'is_module' => $request->attributes->get(LegacyControllerConstants::IS_MODULE_ATTRIBUTE), ]; $adminController = $this->initController($request, $dispatcherHookParameters); @@ -213,7 +213,7 @@ protected function initController(Request $request, array $dispatcherHookParamet { // Retrieving the controller instantiated in LegacyRouterChecker /** @var AdminController $adminController */ - $adminController = $request->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_INSTANCE_ATTRIBUTE); + $adminController = $request->attributes->get(LegacyControllerConstants::INSTANCE_ATTRIBUTE); // Fill default smarty variables as they can be used in partial templates rendered in init methods $this->assignSmartyVariables->fillDefault(); diff --git a/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php b/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php index b7738e1a1c8d4..aeda1896b4b9e 100644 --- a/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php +++ b/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php @@ -37,7 +37,7 @@ use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint; use PrestaShop\PrestaShop\Core\Util\Url\UrlCleaner; use PrestaShopBundle\Controller\Attribute\AllShopContext; -use PrestaShopBundle\Routing\LegacyRouterChecker; +use PrestaShopBundle\Routing\LegacyControllerConstants; use ReflectionClass; use ReflectionException; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -45,7 +45,6 @@ use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Routing\Exception\NoConfigurationException; use Symfony\Component\Routing\RouterInterface; -use PrestaShopBundle\Routing\LegacyRouterChecker; /** * Listener dedicated to set up Shop context for the Back-Office/Admin application. @@ -117,13 +116,14 @@ private function getMultiShopConstraint(Request $request): ShopConstraint return $shopConstraint; } - $isAllShopContext = $request->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_IS_ALL_SHOP_CONTEXT); + $shopConstraint = ShopConstraint::allShops(); + // Check if the displayed legacy controller forces All shops mode (check already performed by LegacyRouterChecker) + $isAllShopContext = $request->attributes->get(LegacyControllerConstants::IS_ALL_SHOP_CONTEXT_ATTRIBUTE); if ($isAllShopContext) { return $shopConstraint; } - $shopConstraint = ShopConstraint::allShops(); $cookieShopConstraint = $this->getShopConstraintFromCookie(); if ($cookieShopConstraint) { if ($cookieShopConstraint->getShopGroupId()) { diff --git a/src/PrestaShopBundle/EventListener/Admin/TokenizedUrlsListener.php b/src/PrestaShopBundle/EventListener/Admin/TokenizedUrlsListener.php index e4b5293ecbaf9..7ac8ac2cd9d23 100644 --- a/src/PrestaShopBundle/EventListener/Admin/TokenizedUrlsListener.php +++ b/src/PrestaShopBundle/EventListener/Admin/TokenizedUrlsListener.php @@ -28,7 +28,7 @@ use PrestaShop\PrestaShop\Core\Feature\TokenInUrls; use PrestaShop\PrestaShop\Core\Util\Url\UrlCleaner; -use PrestaShopBundle\Routing\LegacyRouterChecker; +use PrestaShopBundle\Routing\LegacyControllerConstants; use PrestaShopBundle\Security\Admin\UserTokenManager; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpKernel\Event\KernelEvent; @@ -51,7 +51,7 @@ public function __construct( public function onKernelRequest(KernelEvent $event) { $request = $event->getRequest(); - $publicLegacyRoute = $event->getRequest()->attributes->get(LegacyRouterChecker::LEGACY_CONTROLLER_ANONYMOUS_ATTRIBUTE); + $publicLegacyRoute = $event->getRequest()->attributes->get(LegacyControllerConstants::ANONYMOUS_ATTRIBUTE); if (TokenInUrls::isDisabled() || $publicLegacyRoute) { return; diff --git a/src/PrestaShopBundle/Routing/LegacyControllerConstants.php b/src/PrestaShopBundle/Routing/LegacyControllerConstants.php new file mode 100644 index 0000000000000..ea26287f57e08 --- /dev/null +++ b/src/PrestaShopBundle/Routing/LegacyControllerConstants.php @@ -0,0 +1,14 @@ +init(); - $request->attributes->set(self::LEGACY_CONTROLLER_INSTANCE_ATTRIBUTE, $adminController); - $request->attributes->set(self::LEGACY_CONTROLLER_ANONYMOUS_ATTRIBUTE, $adminController->isAnonymousAllowed()); - $request->attributes->set(self::LEGACY_CONTROLLER_IS_ALL_SHOP_CONTEXT, $adminController->multishop_context === ShopConstraint::ALL_SHOPS); - $request->attributes->set(self::LEGACY_CONTROLLER_CLASS_ATTRIBUTE, $controllerClass); - $request->attributes->set(self::LEGACY_CONTROLLER_IS_MODULE_ATTRIBUTE, $isModule); + $request->attributes->set(LegacyControllerConstants::INSTANCE_ATTRIBUTE, $adminController); + $request->attributes->set(LegacyControllerConstants::ANONYMOUS_ATTRIBUTE, $adminController->isAnonymousAllowed()); + $request->attributes->set(LegacyControllerConstants::IS_ALL_SHOP_CONTEXT_ATTRIBUTE, $adminController->multishop_context === ShopConstraint::ALL_SHOPS); + $request->attributes->set(LegacyControllerConstants::CLASS_ATTRIBUTE, $controllerClass); + $request->attributes->set(LegacyControllerConstants::IS_MODULE_ATTRIBUTE, $isModule); return true; } From d854f4f0ec6c9623fcc27b5a4140c0f3968beaba Mon Sep 17 00:00:00 2001 From: morgan Date: Tue, 16 Apr 2024 11:59:59 +0200 Subject: [PATCH 5/7] Add new priorities and duplicate context listeners for symfony layout --- app/config/admin/services.yml | 58 +++++++++++++++++-- .../Admin/Context/CountryContextListener.php | 10 +++- .../Admin/Context/CurrencyContextListener.php | 8 +++ .../Admin/Context/EmployeeContextListener.php | 8 +++ .../Admin/Context/LanguageContextListener.php | 8 +++ .../Admin/Context/ShopContextListener.php | 9 +++ .../Context/CountryContextListenerTest.php | 2 + .../Context/CurrencyContextListenerTest.php | 2 + .../Context/EmployeeContextListenerTest.php | 12 +++- .../Context/LanguageContextListenerTest.php | 4 ++ .../Admin/Context/ShopContextListenerTest.php | 12 +++- .../ContextEventListenerTestCase.php | 12 ++++ 12 files changed, 134 insertions(+), 11 deletions(-) diff --git a/app/config/admin/services.yml b/app/config/admin/services.yml index caad57d5e04e0..7fb4a358514a0 100644 --- a/app/config/admin/services.yml +++ b/app/config/admin/services.yml @@ -1,10 +1,15 @@ # Dedicated services for Admin app parameters: - # All admin context listeners should be executed before the router listener, this way even if no symfony route is found they context are initialized + # We set priorities lower than the RouterListener (32) so that the LegacyRouterChecker class is called first. + # The correct behaviour of Context Listeners on legacy routes depends on LegacyRouterChecker. + employee_listener_priority: 31 + language_listener_priority: 30 + default_listener_priority: 29 + # All admin context listeners should be executed before the router listener, this way even if no symfony route is found the contexts are initialized correctly # and can be used in admin legacy pages as well (router listener priority is 32) - employee_listener_priority: 35 - language_listener_priority: 34 - default_listener_priority: 33 + legacy_employee_listener_priority: 35 + legacy_language_listener_priority: 34 + legacy_default_listener_priority: 33 services: _defaults: @@ -75,26 +80,71 @@ services: # Employee context must have a higher priority because Shop and Language depend on it PrestaShopBundle\EventListener\Admin\Context\EmployeeContextListener: + arguments: + $isSymfonyLayout: true tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%employee_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\EmployeeContextListener.legacy: + class: PrestaShopBundle\EventListener\Admin\Context\EmployeeContextListener + arguments: + $isSymfonyLayout: false + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%legacy_employee_listener_priority%' } + # Language depends on Employee so its priority is lower, however it has higher priority than Currency and Country because they depend on Language PrestaShopBundle\EventListener\Admin\Context\LanguageContextListener: + arguments: + $isSymfonyLayout: true tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%language_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\LanguageContextListener.legacy: + class: PrestaShopBundle\EventListener\Admin\Context\LanguageContextListener + arguments: + $isSymfonyLayout: false + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%legacy_language_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\ShopContextListener: + arguments: + $isSymfonyLayout: true tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%default_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\ShopContextListener.legacy: + class: PrestaShopBundle\EventListener\Admin\Context\ShopContextListener + arguments: + $isSymfonyLayout: false + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%legacy_default_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\CurrencyContextListener: + arguments: + $isSymfonyLayout: true tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%default_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\CurrencyContextListener.legacy: + class: PrestaShopBundle\EventListener\Admin\Context\CurrencyContextListener + arguments: + $isSymfonyLayout: false + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%legacy_default_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\CountryContextListener: + arguments: + $isSymfonyLayout: true tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%default_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\CountryContextListener.legacy: + class: PrestaShopBundle\EventListener\Admin\Context\CountryContextListener + arguments: + $isSymfonyLayout: false + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%legacy_default_listener_priority%' } + # This listener must run after the router listener since it's based on the request attributes that are defined by the router PrestaShopBundle\EventListener\Admin\Context\LegacyControllerContextListener: tags: diff --git a/src/PrestaShopBundle/EventListener/Admin/Context/CountryContextListener.php b/src/PrestaShopBundle/EventListener/Admin/Context/CountryContextListener.php index e061f5d517fce..0f778872064a6 100644 --- a/src/PrestaShopBundle/EventListener/Admin/Context/CountryContextListener.php +++ b/src/PrestaShopBundle/EventListener/Admin/Context/CountryContextListener.php @@ -30,6 +30,8 @@ use PrestaShop\PrestaShop\Core\ConfigurationInterface; use PrestaShop\PrestaShop\Core\Context\CountryContextBuilder; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; /** @@ -39,7 +41,9 @@ class CountryContextListener { public function __construct( private readonly CountryContextBuilder $countryContextBuilder, - private readonly ConfigurationInterface $configuration + private readonly ConfigurationInterface $configuration, + private readonly FeatureFlagStateCheckerInterface $featureFlagStateChecker, + private readonly bool $isSymfonyLayout, ) { } @@ -49,6 +53,10 @@ public function onKernelRequest(RequestEvent $event): void return; } + if ($this->isSymfonyLayout !== $this->featureFlagStateChecker->isEnabled(FeatureFlagSettings::FEATURE_FLAG_SYMFONY_LAYOUT)) { + return; + } + $this->countryContextBuilder->setCountryId((int) $this->configuration->get('PS_COUNTRY_DEFAULT')); } } diff --git a/src/PrestaShopBundle/EventListener/Admin/Context/CurrencyContextListener.php b/src/PrestaShopBundle/EventListener/Admin/Context/CurrencyContextListener.php index 41e5f53d24522..916fa46f8394b 100644 --- a/src/PrestaShopBundle/EventListener/Admin/Context/CurrencyContextListener.php +++ b/src/PrestaShopBundle/EventListener/Admin/Context/CurrencyContextListener.php @@ -30,6 +30,8 @@ use PrestaShop\PrestaShop\Core\ConfigurationInterface; use PrestaShop\PrestaShop\Core\Context\CurrencyContextBuilder; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; /** @@ -40,6 +42,8 @@ class CurrencyContextListener public function __construct( private readonly CurrencyContextBuilder $currencyContextBuilder, private readonly ConfigurationInterface $configuration, + private readonly FeatureFlagStateCheckerInterface $featureFlagStateChecker, + private readonly bool $isSymfonyLayout, ) { } @@ -49,6 +53,10 @@ public function onKernelRequest(RequestEvent $event): void return; } + if ($this->isSymfonyLayout !== $this->featureFlagStateChecker->isEnabled(FeatureFlagSettings::FEATURE_FLAG_SYMFONY_LAYOUT)) { + return; + } + $this->currencyContextBuilder->setCurrencyId((int) $this->configuration->get('PS_CURRENCY_DEFAULT')); } } diff --git a/src/PrestaShopBundle/EventListener/Admin/Context/EmployeeContextListener.php b/src/PrestaShopBundle/EventListener/Admin/Context/EmployeeContextListener.php index 6387bcdcc55a1..c17c94ba9162d 100644 --- a/src/PrestaShopBundle/EventListener/Admin/Context/EmployeeContextListener.php +++ b/src/PrestaShopBundle/EventListener/Admin/Context/EmployeeContextListener.php @@ -30,6 +30,8 @@ use PrestaShop\PrestaShop\Adapter\LegacyContext; use PrestaShop\PrestaShop\Core\Context\EmployeeContextBuilder; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; use PrestaShopBundle\Security\Admin\Employee; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -43,6 +45,8 @@ public function __construct( private readonly EmployeeContextBuilder $employeeContextBuilder, private readonly LegacyContext $legacyContext, private readonly Security $security, + private readonly FeatureFlagStateCheckerInterface $featureFlagStateChecker, + private readonly bool $isSymfonyLayout, ) { } @@ -52,6 +56,10 @@ public function onKernelRequest(RequestEvent $event): void return; } + if ($this->isSymfonyLayout !== $this->featureFlagStateChecker->isEnabled(FeatureFlagSettings::FEATURE_FLAG_SYMFONY_LAYOUT)) { + return; + } + if (!empty($this->legacyContext->getContext()->cookie->id_employee)) { $this->employeeContextBuilder->setEmployeeId((int) $this->legacyContext->getContext()->cookie->id_employee); } elseif ($this->security->getUser() instanceof Employee) { diff --git a/src/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListener.php b/src/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListener.php index 1fa4f0e648b8c..08e7021f57169 100644 --- a/src/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListener.php +++ b/src/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListener.php @@ -31,6 +31,8 @@ use PrestaShop\PrestaShop\Core\ConfigurationInterface; use PrestaShop\PrestaShop\Core\Context\EmployeeContext; use PrestaShop\PrestaShop\Core\Context\LanguageContextBuilder; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; /** @@ -42,6 +44,8 @@ public function __construct( private readonly LanguageContextBuilder $languageContextBuilder, private readonly EmployeeContext $employeeContext, private readonly ConfigurationInterface $configuration, + private readonly FeatureFlagStateCheckerInterface $featureFlagStateChecker, + private readonly bool $isSymfonyLayout, ) { } @@ -51,6 +55,10 @@ public function onKernelRequest(RequestEvent $event): void return; } + if ($this->isSymfonyLayout !== $this->featureFlagStateChecker->isEnabled(FeatureFlagSettings::FEATURE_FLAG_SYMFONY_LAYOUT)) { + return; + } + $defaultLanguageId = (int) $this->configuration->get('PS_LANG_DEFAULT'); $this->languageContextBuilder->setDefaultLanguageId($defaultLanguageId); diff --git a/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php b/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php index aeda1896b4b9e..9c575a222ed42 100644 --- a/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php +++ b/src/PrestaShopBundle/EventListener/Admin/Context/ShopContextListener.php @@ -35,6 +35,8 @@ use PrestaShop\PrestaShop\Core\Domain\Configuration\ShopConfigurationInterface; use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\ShopException; use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; use PrestaShop\PrestaShop\Core\Util\Url\UrlCleaner; use PrestaShopBundle\Controller\Attribute\AllShopContext; use PrestaShopBundle\Routing\LegacyControllerConstants; @@ -58,6 +60,8 @@ public function __construct( private readonly LegacyContext $legacyContext, private readonly MultistoreFeature $multistoreFeature, private readonly RouterInterface $router, + private readonly FeatureFlagStateCheckerInterface $featureFlagStateChecker, + private readonly bool $isSymfonyLayout, ) { } @@ -70,6 +74,11 @@ public function onKernelRequest(RequestEvent $event): void if (!$event->isMainRequest()) { return; } + + if ($this->isSymfonyLayout !== $this->featureFlagStateChecker->isEnabled(FeatureFlagSettings::FEATURE_FLAG_SYMFONY_LAYOUT)) { + return; + } + $psSslEnabled = (bool) $this->configuration->get('PS_SSL_ENABLED', null, ShopConstraint::allShops()); $this->shopContextBuilder->setSecureMode($psSslEnabled && $event->getRequest()->isSecure()); diff --git a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/CountryContextListenerTest.php b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/CountryContextListenerTest.php index 4d38cc54b66d0..9ca90cf155600 100644 --- a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/CountryContextListenerTest.php +++ b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/CountryContextListenerTest.php @@ -48,6 +48,8 @@ public function testKernelRequest(): void $listener = new CountryContextListener( $countryContextBuilder, $this->mockConfiguration(['PS_COUNTRY_DEFAULT' => 42]), + $this->mockFeatureFlagStateChecker(), + true, ); $event = $this->createRequestEvent(new Request()); diff --git a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/CurrencyContextListenerTest.php b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/CurrencyContextListenerTest.php index d4cb44eff8269..0a93688d0c88f 100644 --- a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/CurrencyContextListenerTest.php +++ b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/CurrencyContextListenerTest.php @@ -48,6 +48,8 @@ public function testKernelRequest(): void $listener = new CurrencyContextListener( $currencyContextBuilder, $this->mockConfiguration(['PS_CURRENCY_DEFAULT' => 42]), + $this->mockFeatureFlagStateChecker(), + true, ); $event = $this->createRequestEvent(new Request()); diff --git a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/EmployeeContextListenerTest.php b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/EmployeeContextListenerTest.php index cabfc0761c9ed..e82f9dc50b3cc 100644 --- a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/EmployeeContextListenerTest.php +++ b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/EmployeeContextListenerTest.php @@ -46,7 +46,9 @@ public function testFindEmployee(): void $listener = new EmployeeContextListener( $employeeBuilder, $this->mockLegacyContext(['id_employee' => 42]), - $this->createMock(Security::class) + $this->createMock(Security::class), + $this->mockFeatureFlagStateChecker(), + true, ); $event = $this->createRequestEvent(new Request()); @@ -63,7 +65,9 @@ public function testEmployeeNotFound(): void $listener = new EmployeeContextListener( $employeeBuilder, $this->mockLegacyContext(['id_employee' => null]), - $this->createMock(Security::class) + $this->createMock(Security::class), + $this->mockFeatureFlagStateChecker(), + true, ); $listener->onKernelRequest($event); $this->assertEquals(null, $this->getPrivateField($employeeBuilder, 'employeeId')); @@ -81,7 +85,9 @@ public function testEmployeeFromSymfonySecurity(): void $listener = new EmployeeContextListener( $employeeBuilder, $this->mockLegacyContext(['id_employee' => null]), - $securityMock + $securityMock, + $this->mockFeatureFlagStateChecker(), + true, ); $event = $this->createRequestEvent(new Request()); diff --git a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListenerTest.php b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListenerTest.php index ccaef27af1201..722000dcab7cc 100644 --- a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListenerTest.php +++ b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListenerTest.php @@ -57,6 +57,8 @@ public function testContextEmployeeLanguage(): void $languageContextBuilder, $this->mockEmployeeContext(self::EMPLOYEE_CONTEXT_LANGUAGE_ID), $this->mockConfiguration(), + $this->mockFeatureFlagStateChecker(), + true, ); $event = $this->createRequestEvent(new Request()); @@ -76,6 +78,8 @@ public function testDefaultConfigurationLanguage(): void $languageContextBuilder, $this->mockEmployeeContext(null), $this->mockConfiguration(['PS_LANG_DEFAULT' => self::DEFAULT_CONFIGURATION_LANGUAGE_ID]), + $this->mockFeatureFlagStateChecker(), + true, ); $event = $this->createRequestEvent(new Request()); diff --git a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/ShopContextListenerTest.php b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/ShopContextListenerTest.php index cf841b07cae7f..9643db9451929 100644 --- a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/ShopContextListenerTest.php +++ b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/ShopContextListenerTest.php @@ -61,7 +61,9 @@ public function testSingleShop(): void $this->mockConfiguration(['PS_SHOP_DEFAULT' => self::DEFAULT_SHOP_ID, 'PS_SSL_ENABLED' => self::PS_SSL_ENABLED]), $this->mockLegacyContext(['shopContext' => '']), $this->mockMultistoreFeature(false), - $this->mockRouter() + $this->mockRouter(), + $this->mockFeatureFlagStateChecker(), + true, ); $listener->onKernelRequest($event); @@ -94,7 +96,9 @@ public function testMultiShop(string $cookieValue, ?array $employeeData, ShopCon $this->mockConfiguration(['PS_SHOP_DEFAULT' => self::DEFAULT_SHOP_ID, 'PS_SSL_ENABLED' => self::PS_SSL_ENABLED]), $this->mockLegacyContext(['shopContext' => $cookieValue]), $this->mockMultistoreFeature(true), - $this->mockRouter() + $this->mockRouter(), + $this->mockFeatureFlagStateChecker(), + true, ); $listener->onKernelRequest($event); @@ -169,7 +173,9 @@ public function testMultiShopRedirection(string $switchParameterValue, ?string $ $this->mockConfiguration(['PS_SHOP_DEFAULT' => self::DEFAULT_SHOP_ID, 'PS_SSL_ENABLED' => self::PS_SSL_ENABLED]), $mockContext, $this->mockMultistoreFeature(true), - $this->mockRouter() + $this->mockRouter(), + $this->mockFeatureFlagStateChecker(), + true, ); // Check that initially the cookie has a null value diff --git a/tests/Unit/PrestaShopBundle/EventListener/ContextEventListenerTestCase.php b/tests/Unit/PrestaShopBundle/EventListener/ContextEventListenerTestCase.php index da48809efbc5f..3f1b80435dae7 100644 --- a/tests/Unit/PrestaShopBundle/EventListener/ContextEventListenerTestCase.php +++ b/tests/Unit/PrestaShopBundle/EventListener/ContextEventListenerTestCase.php @@ -34,6 +34,7 @@ use PrestaShop\PrestaShop\Adapter\LegacyContext; use PrestaShop\PrestaShop\Adapter\Shop\Repository\ShopRepository; use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId; +use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; use ReflectionProperty; use Shop; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -82,6 +83,17 @@ protected function mockLegacyContext(array $cookieValues): LegacyContext|MockObj return $legacyContext; } + protected function mockFeatureFlagStateChecker(): FeatureFlagStateCheckerInterface|MockObject + { + $featureFlagStateChecker = $this->createMock(FeatureFlagStateCheckerInterface::class); + $featureFlagStateChecker + ->method('isEnabled') + ->willReturn(true) + ; + + return $featureFlagStateChecker; + } + protected function createRequestEvent(Request $request): RequestEvent { return new RequestEvent(static::createKernel(), $request, HttpKernelInterface::MAIN_REQUEST); From 128cd449152cad3088e70a81789f56b64066c8c4 Mon Sep 17 00:00:00 2001 From: morgan Date: Wed, 17 Apr 2024 11:36:30 +0200 Subject: [PATCH 6/7] Add default Context listener --- app/config/admin/services.yml | 8 +++ .../DefaultLanguageContextListener.php | 58 +++++++++++++++++ .../Context/DefaultShopContextListener.php | 62 +++++++++++++++++++ .../Admin/Context/LanguageContextListener.php | 9 --- .../Routing/LegacyRouterChecker.php | 6 +- .../Context/LanguageContextListenerTest.php | 7 +-- 6 files changed, 133 insertions(+), 17 deletions(-) create mode 100644 src/PrestaShopBundle/EventListener/Admin/Context/DefaultLanguageContextListener.php create mode 100644 src/PrestaShopBundle/EventListener/Admin/Context/DefaultShopContextListener.php diff --git a/app/config/admin/services.yml b/app/config/admin/services.yml index 7fb4a358514a0..9b43c04b87c65 100644 --- a/app/config/admin/services.yml +++ b/app/config/admin/services.yml @@ -92,6 +92,10 @@ services: tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%legacy_employee_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\DefaultLanguageContextListener: + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%legacy_language_listener_priority%' } + # Language depends on Employee so its priority is lower, however it has higher priority than Currency and Country because they depend on Language PrestaShopBundle\EventListener\Admin\Context\LanguageContextListener: arguments: @@ -106,6 +110,10 @@ services: tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%legacy_language_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\DefaultShopContextListener: + tags: + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: '%legacy_default_listener_priority%' } + PrestaShopBundle\EventListener\Admin\Context\ShopContextListener: arguments: $isSymfonyLayout: true diff --git a/src/PrestaShopBundle/EventListener/Admin/Context/DefaultLanguageContextListener.php b/src/PrestaShopBundle/EventListener/Admin/Context/DefaultLanguageContextListener.php new file mode 100644 index 0000000000000..3371e2a9a14eb --- /dev/null +++ b/src/PrestaShopBundle/EventListener/Admin/Context/DefaultLanguageContextListener.php @@ -0,0 +1,58 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + */ + +declare(strict_types=1); + +namespace PrestaShopBundle\EventListener\Admin\Context; + +use PrestaShop\PrestaShop\Core\ConfigurationInterface; +use PrestaShop\PrestaShop\Core\Context\LanguageContextBuilder; +use Symfony\Component\HttpKernel\Event\RequestEvent; + +/** + * Listener dedicated to set up default Language context for the Back-Office/Admin application. + * We need to initialize the LanguageContext earlier because it's used by components in the Symfony + * layout that will be displayed in the Not Found legacy page + */ +class DefaultLanguageContextListener +{ + public function __construct( + private readonly LanguageContextBuilder $languageContextBuilder, + private readonly ConfigurationInterface $configuration, + ) { + } + + public function onKernelRequest(RequestEvent $event): void + { + if (!$event->isMainRequest()) { + return; + } + + $defaultLanguageId = (int) $this->configuration->get('PS_LANG_DEFAULT'); + $this->languageContextBuilder->setDefaultLanguageId($defaultLanguageId); + $this->languageContextBuilder->setLanguageId($defaultLanguageId); + } +} diff --git a/src/PrestaShopBundle/EventListener/Admin/Context/DefaultShopContextListener.php b/src/PrestaShopBundle/EventListener/Admin/Context/DefaultShopContextListener.php new file mode 100644 index 0000000000000..dc8edba59dfed --- /dev/null +++ b/src/PrestaShopBundle/EventListener/Admin/Context/DefaultShopContextListener.php @@ -0,0 +1,62 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + */ + +declare(strict_types=1); + +namespace PrestaShopBundle\EventListener\Admin\Context; + +use PrestaShop\PrestaShop\Core\Context\ShopContextBuilder; +use PrestaShop\PrestaShop\Core\Domain\Configuration\ShopConfigurationInterface; +use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\ShopException; +use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint; +use Symfony\Component\HttpKernel\Event\RequestEvent; + +/** + * Listener dedicated to set up default Shop context for the Back-Office/Admin application. + * We need to initialize the ShopContext earlier because it's used by components in the Symfony + * layout that will be displayed in the Not Found legacy page + */ +class DefaultShopContextListener +{ + public function __construct( + private readonly ShopContextBuilder $shopContextBuilder, + private readonly ShopConfigurationInterface $configuration, + ) { + } + + /** + * @throws ShopException + */ + public function onKernelRequest(RequestEvent $event): void + { + if (!$event->isMainRequest()) { + return; + } + $shopConstraint = ShopConstraint::shop((int) $this->configuration->get('PS_SHOP_DEFAULT', null, ShopConstraint::allShops())); + $this->shopContextBuilder->setShopId($shopConstraint->getShopId()->getValue()); + $this->shopContextBuilder->setShopConstraint($shopConstraint); + } +} diff --git a/src/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListener.php b/src/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListener.php index 08e7021f57169..41fd9aec7bc9c 100644 --- a/src/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListener.php +++ b/src/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListener.php @@ -28,7 +28,6 @@ namespace PrestaShopBundle\EventListener\Admin\Context; -use PrestaShop\PrestaShop\Core\ConfigurationInterface; use PrestaShop\PrestaShop\Core\Context\EmployeeContext; use PrestaShop\PrestaShop\Core\Context\LanguageContextBuilder; use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; @@ -43,7 +42,6 @@ class LanguageContextListener public function __construct( private readonly LanguageContextBuilder $languageContextBuilder, private readonly EmployeeContext $employeeContext, - private readonly ConfigurationInterface $configuration, private readonly FeatureFlagStateCheckerInterface $featureFlagStateChecker, private readonly bool $isSymfonyLayout, ) { @@ -58,16 +56,9 @@ public function onKernelRequest(RequestEvent $event): void if ($this->isSymfonyLayout !== $this->featureFlagStateChecker->isEnabled(FeatureFlagSettings::FEATURE_FLAG_SYMFONY_LAYOUT)) { return; } - - $defaultLanguageId = (int) $this->configuration->get('PS_LANG_DEFAULT'); - $this->languageContextBuilder->setDefaultLanguageId($defaultLanguageId); - if ($this->employeeContext->getEmployee()) { // Use the employee language if available $this->languageContextBuilder->setLanguageId($this->employeeContext->getEmployee()->getLanguageId()); - } else { - // If not use the default language of the shop - $this->languageContextBuilder->setLanguageId($defaultLanguageId); } } } diff --git a/src/PrestaShopBundle/Routing/LegacyRouterChecker.php b/src/PrestaShopBundle/Routing/LegacyRouterChecker.php index 317895c0d0729..2c80277498ba0 100644 --- a/src/PrestaShopBundle/Routing/LegacyRouterChecker.php +++ b/src/PrestaShopBundle/Routing/LegacyRouterChecker.php @@ -109,10 +109,10 @@ public function check(Request $request): bool // It's clearer to actually return a not found exception, for now the dispatcher is still used as fallback in index.php // but when it's cleared and only Symfony handles the whole routing then we can display a proper not found Symfony page if (!isset($controllers[strtolower($queryController)])) { - throw new NotFoundHttpException(sprintf('Unknown controller %s', $queryController)); + $controllerClass = 'AdminNotFoundController'; + } else { + $controllerClass = $controllers[strtolower($queryController)]; } - - $controllerClass = $controllers[strtolower($queryController)]; } // We load the controller early in the process (during router matching actually), because the controller // configuration has many impacts on the contexts, the security listeners, ... And the relevant data can diff --git a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListenerTest.php b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListenerTest.php index 722000dcab7cc..b94c80a031c7e 100644 --- a/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListenerTest.php +++ b/tests/Unit/PrestaShopBundle/EventListener/Admin/Context/LanguageContextListenerTest.php @@ -36,6 +36,7 @@ use PrestaShop\PrestaShop\Core\Context\LanguageContextBuilder; use PrestaShop\PrestaShop\Core\Language\LanguageRepositoryInterface; use PrestaShop\PrestaShop\Core\Localization\Locale\RepositoryInterface; +use PrestaShopBundle\EventListener\Admin\Context\DefaultLanguageContextListener; use PrestaShopBundle\EventListener\Admin\Context\LanguageContextListener; use Symfony\Component\HttpFoundation\Request; use Tests\Unit\PrestaShopBundle\EventListener\ContextEventListenerTestCase; @@ -56,7 +57,6 @@ public function testContextEmployeeLanguage(): void $listener = new LanguageContextListener( $languageContextBuilder, $this->mockEmployeeContext(self::EMPLOYEE_CONTEXT_LANGUAGE_ID), - $this->mockConfiguration(), $this->mockFeatureFlagStateChecker(), true, ); @@ -74,12 +74,9 @@ public function testDefaultConfigurationLanguage(): void $this->createMock(ContextStateManager::class), $this->createMock(ObjectModelLanguageRepository::class) ); - $listener = new LanguageContextListener( + $listener = new DefaultLanguageContextListener( $languageContextBuilder, - $this->mockEmployeeContext(null), $this->mockConfiguration(['PS_LANG_DEFAULT' => self::DEFAULT_CONFIGURATION_LANGUAGE_ID]), - $this->mockFeatureFlagStateChecker(), - true, ); $event = $this->createRequestEvent(new Request()); From 6e4e26d596b83192121d619d357243d333287d60 Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Tue, 23 Apr 2024 16:42:05 +0200 Subject: [PATCH 7/7] Functional Tests: Fixed `regression/menu/deniedAccessToModuleCatalogPage.ts` --- tests/UI/pages/BO/BObasePage.ts | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/UI/pages/BO/BObasePage.ts b/tests/UI/pages/BO/BObasePage.ts index 46cedaa3908e0..a251fd6ff0f54 100644 --- a/tests/UI/pages/BO/BObasePage.ts +++ b/tests/UI/pages/BO/BObasePage.ts @@ -256,9 +256,13 @@ export default class BOBasePage extends CommonPage { private readonly helpDocumentURL: string; - private readonly invalidTokenContinueLink: string; + private readonly invalidTokenContinueLinkLegacy: string; - private readonly invalidTokenCancelLink: string; + private readonly invalidTokenCancelLinkLegacy: string; + + private readonly invalidTokenContinueLinkSymfony: string; + + private readonly invalidTokenCancelLinkSymfony: string; public readonly debugModeToolbar: string; @@ -613,8 +617,10 @@ export default class BOBasePage extends CommonPage { this.helpDocumentURL = `${this.rightSidebar} div.quicknav-scroller._fullspace object`; // Invalid token block - this.invalidTokenContinueLink = 'a.btn-continue'; - this.invalidTokenCancelLink = 'a.btn-cancel'; + this.invalidTokenContinueLinkLegacy = 'a.btn-continue'; + this.invalidTokenCancelLinkLegacy = 'a.btn-cancel'; + this.invalidTokenContinueLinkSymfony = '#security-compromised-page #csrf-white-container div a:nth-child(1)'; + this.invalidTokenCancelLinkSymfony = '#security-compromised-page #csrf-white-container div a:nth-child(2)'; } /* @@ -1150,10 +1156,18 @@ export default class BOBasePage extends CommonPage { */ async navigateToPageWithInvalidToken(page: Page, url: string, continueToPage: boolean = true): Promise { await this.goTo(page, url); - if (await this.elementVisible(page, this.invalidTokenContinueLink, 10000)) { + // Legacy Layout + if (await this.elementVisible(page, this.invalidTokenContinueLinkLegacy, 10000)) { + await this.clickAndWaitForURL( + page, + continueToPage ? this.invalidTokenContinueLinkLegacy : this.invalidTokenCancelLinkLegacy, + ); + } + // Symfony Layout + if (await this.elementVisible(page, this.invalidTokenContinueLinkSymfony, 10000)) { await this.clickAndWaitForURL( page, - continueToPage ? this.invalidTokenContinueLink : this.invalidTokenCancelLink, + continueToPage ? this.invalidTokenContinueLinkSymfony : this.invalidTokenCancelLinkSymfony, ); } }