From ec9c3f99e15336dc4f6877f512300f231c17c6da Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Mon, 4 Mar 2024 13:53:32 +0100 Subject: [PATCH] Merge pull request from GHSA-jr83-m233-gg6p * Fix setting of Webspace Security System before Symfony RouteListener * Fix Redirect for Security System checker error * Add test case to avoid regression * Add test for none secured pages * Fix some PHP 7.2 issues * Remmove website based system listener test * Activate security only for specific test * Remove old ContentRouteProviderTests * Fix phpcs * Skip security listener test for older symfony security system * Fix code style * Fix phpstan issues * Throw deprecation when requestAnalyzer is still given * Fix code style * Mark new service as internal and final --- phpstan-baseline.neon | 14 +- .../Resources/config/request_analyzer.xml | 6 + .../EventListener/SystemListener.php | 19 +-- .../Resources/config/services.xml | 2 +- .../Unit/EventListener/SystemListenerTest.php | 29 ---- .../EventListener/RouterListener.php | 1 + .../EventListener/SecurityListener.php | 83 +++++++++ .../Resources/config/services.xml | 7 + .../Resources/config/website.xml | 2 +- .../Routing/ContentRouteProvider.php | 16 +- .../Tests/Application/Kernel.php | 41 ++++- .../config/config_with_security.yml | 40 +++++ .../Application/config/routing_website.yml | 6 + .../Application/config/webspaces/sulu_lo.xml | 1 - .../EventListener/SecurityListenerTest.php | 152 +++++++++++++++++ .../Unit/Routing/ContentRouteProviderTest.php | 158 ------------------ .../Attributes/SystemRequestProcessor.php | 76 +++++++++ 17 files changed, 429 insertions(+), 224 deletions(-) create mode 100644 src/Sulu/Bundle/WebsiteBundle/EventListener/SecurityListener.php create mode 100644 src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/config_with_security.yml create mode 100644 src/Sulu/Bundle/WebsiteBundle/Tests/Functional/EventListener/SecurityListenerTest.php create mode 100644 src/Sulu/Component/Webspace/Analyzer/Attributes/SystemRequestProcessor.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index bea5b36cd13..95390ef9363 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -29621,12 +29621,12 @@ parameters: path: src/Sulu/Bundle/SecurityBundle/EventListener/SuluSecurityListener.php - - message: "#^If condition is always true\\.$#" + message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#" count: 1 path: src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php - - message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#" + message: "#^Property Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListener\\:\\:\\$requestAnalyzer is never read, only written\\.$#" count: 1 path: src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php @@ -30550,11 +30550,6 @@ parameters: count: 1 path: src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SuluSecurityListenerTest.php - - - message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListenerTest\\:\\:provideSetWebsiteSystem\\(\\) has no return type specified\\.$#" - count: 1 - path: src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php - - message: "#^Class Sulu\\\\Bundle\\\\SecurityBundle\\\\Security\\\\AuthenticationEntryPoint does not have a constructor and must be instantiated without any parameters\\.$#" count: 1 @@ -34920,6 +34915,11 @@ parameters: count: 1 path: src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php + - + message: "#^Property Sulu\\\\Bundle\\\\WebsiteBundle\\\\Routing\\\\ContentRouteProvider\\:\\:\\$securityChecker is never read, only written\\.$#" + count: 1 + path: src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php + - message: "#^Strict comparison using \\=\\=\\= between null and string will always evaluate to false\\.$#" count: 1 diff --git a/src/Sulu/Bundle/CoreBundle/Resources/config/request_analyzer.xml b/src/Sulu/Bundle/CoreBundle/Resources/config/request_analyzer.xml index fb0e06f8f99..4656971c50f 100644 --- a/src/Sulu/Bundle/CoreBundle/Resources/config/request_analyzer.xml +++ b/src/Sulu/Bundle/CoreBundle/Resources/config/request_analyzer.xml @@ -51,6 +51,12 @@ + + + %sulu.context% + + diff --git a/src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php b/src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php index 63ae275f50e..fcc7fadc67e 100644 --- a/src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php +++ b/src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php @@ -27,7 +27,7 @@ class SystemListener implements EventSubscriberInterface private $systemStore; /** - * @var RequestAnalyzerInterface + * @var RequestAnalyzerInterface|null */ private $requestAnalyzer; @@ -38,10 +38,15 @@ class SystemListener implements EventSubscriberInterface public function __construct( SystemStoreInterface $systemStore, - RequestAnalyzerInterface $requestAnalyzer, + ?RequestAnalyzerInterface $requestAnalyzer, string $context ) { $this->systemStore = $systemStore; + + if (null !== $requestAnalyzer) { + @trigger_deprecation('sulu/sulu', '2.4', 'The argument "%s" in class "%s" is deprecated and not longer required set `null` instead.', RequestAnalyzerInterface::class, __CLASS__); + } + $this->requestAnalyzer = $requestAnalyzer; $this->context = $context; } @@ -58,15 +63,5 @@ public function onKernelRequest(RequestEvent $requestEvent) return; } - - $webspace = $this->requestAnalyzer->getWebspace(); - if ($webspace) { - $security = $webspace->getSecurity(); - if ($security) { - $this->systemStore->setSystem($security->getSystem()); - - return; - } - } } } diff --git a/src/Sulu/Bundle/SecurityBundle/Resources/config/services.xml b/src/Sulu/Bundle/SecurityBundle/Resources/config/services.xml index 5c7e716685a..a4fb982e0de 100644 --- a/src/Sulu/Bundle/SecurityBundle/Resources/config/services.xml +++ b/src/Sulu/Bundle/SecurityBundle/Resources/config/services.xml @@ -306,7 +306,7 @@ - + null %sulu.context% diff --git a/src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php b/src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php index 91911c87700..9ba0de60f38 100644 --- a/src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php +++ b/src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php @@ -15,8 +15,6 @@ use Prophecy\Prophecy\ObjectProphecy; use Sulu\Bundle\SecurityBundle\System\SystemStoreInterface; use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface; -use Sulu\Component\Webspace\Security; -use Sulu\Component\Webspace\Webspace; use Symfony\Component\HttpKernel\Event\RequestEvent; class SystemListenerTest extends TestCase @@ -46,33 +44,6 @@ public function testSetAdminSystem(): void $this->systemStore->setSystem('Sulu')->shouldBeCalled(); } - public function provideSetWebsiteSystem() - { - return [ - ['sulu-test'], - ['sulu-blog'], - ]; - } - - /** - * @dataProvider provideSetWebsiteSystem - */ - public function testSetWebsiteSystem(string $system): void - { - $systemListener = $this->createSystemListener('website'); - $requestEvent = $this->prophesize(RequestEvent::class); - - $webspace = new Webspace(); - $security = new Security(); - $security->setSystem($system); - $webspace->setSecurity($security); - $this->requestAnalyzer->getWebspace()->willReturn($webspace); - - $systemListener->onKernelRequest($requestEvent->reveal()); - - $this->systemStore->setSystem($system)->shouldBeCalled(); - } - private function createSystemListener(string $context): SystemListener { return new SystemListener($this->systemStore->reveal(), $this->requestAnalyzer->reveal(), $context); diff --git a/src/Sulu/Bundle/WebsiteBundle/EventListener/RouterListener.php b/src/Sulu/Bundle/WebsiteBundle/EventListener/RouterListener.php index b95e2e4f55e..b33d128a13c 100644 --- a/src/Sulu/Bundle/WebsiteBundle/EventListener/RouterListener.php +++ b/src/Sulu/Bundle/WebsiteBundle/EventListener/RouterListener.php @@ -54,6 +54,7 @@ public function onKernelRequest(RequestEvent $event) // This call is required in all cases, because the default router needs our webspace information // Would be nice to also only call this if the _requestAnalyzer attribute is set, but it's set on the next line $this->requestAnalyzer->analyze($request); + $this->baseRouteListener->onKernelRequest($event); if (false !== $request->attributes->getBoolean(static::REQUEST_ANALYZER, true)) { $this->requestAnalyzer->validate($request); diff --git a/src/Sulu/Bundle/WebsiteBundle/EventListener/SecurityListener.php b/src/Sulu/Bundle/WebsiteBundle/EventListener/SecurityListener.php new file mode 100644 index 00000000000..bc6346e5c34 --- /dev/null +++ b/src/Sulu/Bundle/WebsiteBundle/EventListener/SecurityListener.php @@ -0,0 +1,83 @@ +securityChecker = $securityChecker; + } + + public static function getSubscribedEvents(): array + { + return [ + KernelEvents::REQUEST => [ + ['onKernelRequest', 7], // set the security listener after the firewall and after the routing listener + ], + ]; + } + + public function onKernelRequest(RequestEvent $event): void + { + $request = $event->getRequest(); + + if (null === $this->securityChecker) { + return; + } + + $requestAttributes = $request->attributes->get('_sulu'); + if (!$requestAttributes instanceof RequestAttributes) { + return; + } + + $webspace = $requestAttributes->getAttribute('webspace'); + + $structure = $request->attributes->get('structure'); + if (!$structure instanceof PageBridge) { + return; + } + + $document = $structure->getDocument(); + if (!$document instanceof BasePageDocument) { + return; + } + + if ($webspace->hasWebsiteSecurity()) { + $this->securityChecker->checkPermission( + new SecurityCondition( + 'sulu.webspaces.' . $document->getWebspaceName(), + $document->getLocale(), + \get_class($document), + $document->getUuid() + ), + PermissionTypes::VIEW + ); + } + } +} diff --git a/src/Sulu/Bundle/WebsiteBundle/Resources/config/services.xml b/src/Sulu/Bundle/WebsiteBundle/Resources/config/services.xml index 36dbe42159c..5a17af96060 100644 --- a/src/Sulu/Bundle/WebsiteBundle/Resources/config/services.xml +++ b/src/Sulu/Bundle/WebsiteBundle/Resources/config/services.xml @@ -299,6 +299,13 @@ + + + + + + diff --git a/src/Sulu/Bundle/WebsiteBundle/Resources/config/website.xml b/src/Sulu/Bundle/WebsiteBundle/Resources/config/website.xml index 477eb511d7a..d802201ca2b 100644 --- a/src/Sulu/Bundle/WebsiteBundle/Resources/config/website.xml +++ b/src/Sulu/Bundle/WebsiteBundle/Resources/config/website.xml @@ -21,7 +21,7 @@ - + null diff --git a/src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php b/src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php index d5136ae85d1..b8ac82148b1 100644 --- a/src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php +++ b/src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php @@ -24,9 +24,7 @@ use Sulu\Component\Content\Exception\ResourceLocatorNotFoundException; use Sulu\Component\Content\Types\ResourceLocator\Strategy\ResourceLocatorStrategyPoolInterface; use Sulu\Component\DocumentManager\DocumentManagerInterface; -use Sulu\Component\Security\Authorization\PermissionTypes; use Sulu\Component\Security\Authorization\SecurityCheckerInterface; -use Sulu\Component\Security\Authorization\SecurityCondition; use Sulu\Component\Webspace\Analyzer\Attributes\RequestAttributes; use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface; use Sulu\Component\Webspace\Manager\WebspaceManagerInterface; @@ -34,6 +32,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; +use Webmozart\Assert\Assert; /** * The PortalRouteProvider should load the dynamic routes created by Sulu. @@ -97,6 +96,7 @@ public function __construct( $this->webspaceManager = $webspaceManager; $this->requestAnalyzer = $requestAnalyzer; $this->securityChecker = $securityChecker; + Assert::null($securityChecker, 'The security checker should be called by the SecurityListener not the ContentRouteProvider.'); // people who overwrite the ContentRouteProvider should make aware of that they also need to refactor this $this->defaultOptions = $defaultOptions; } @@ -166,18 +166,6 @@ public function getRouteCollectionForRequest(Request $request) return $collection; } - if ($this->securityChecker && $portal->getWebspace()->hasWebsiteSecurity()) { - $this->securityChecker->checkPermission( - new SecurityCondition( - 'sulu.webspaces.' . $document->getWebspaceName(), - $document->getLocale(), - \get_class($document), - $document->getUuid() - ), - PermissionTypes::VIEW - ); - } - if (\preg_match('/\/$/', $resourceLocator) && ('/' !== $resourceLocator || $prefix)) { // redirect page to page without slash at the end $url = $prefix . \rtrim($resourceLocator, '/'); diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/Kernel.php b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/Kernel.php index 4c654d0486d..089b8fb1b13 100644 --- a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/Kernel.php +++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/Kernel.php @@ -12,10 +12,16 @@ namespace Sulu\Bundle\WebsiteBundle\Tests\Application; use Sulu\Bundle\TestBundle\Kernel\SuluTestKernel; +use Symfony\Bundle\SecurityBundle\SecurityBundle; use Symfony\Component\Config\Loader\LoaderInterface; class Kernel extends SuluTestKernel { + /** + * @var string + */ + private $appContext; + /** * @param string $environment * @param bool $debug @@ -23,7 +29,10 @@ class Kernel extends SuluTestKernel */ public function __construct($environment, $debug, $suluContext = self::CONTEXT_ADMIN) { - parent::__construct($environment, $debug, $suluContext); + $envParts = \explode('_', $environment, 2); + $this->appContext = $envParts[1] ?? ''; + + parent::__construct($envParts[0], $debug, $suluContext); } public function registerContainerConfiguration(LoaderInterface $loader): void @@ -32,5 +41,35 @@ public function registerContainerConfiguration(LoaderInterface $loader): void $context = $this->getContext(); $loader->load(__DIR__ . '/config/config_' . $context . '.yml'); + if ('' !== $this->appContext) { + $loader->load(__DIR__ . '/config/config_' . $this->appContext . '.yml'); + } + } + + public function registerBundles(): iterable + { + $bundles = parent::registerBundles(); + + if ('with_security' === $this->appContext) { + $bundles[] = new SecurityBundle(); + } + + return $bundles; + } + + /** + * @return string + */ + public function getCacheDir() + { + return parent::getCacheDir() . \ltrim('/' . $this->appContext); + } + + /** + * @return string + */ + public function getCommonCacheDir() + { + return parent::getCommonCacheDir() . \ltrim('/' . $this->appContext); } } diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/config_with_security.yml b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/config_with_security.yml new file mode 100644 index 00000000000..1611557269b --- /dev/null +++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/config_with_security.yml @@ -0,0 +1,40 @@ +security: + enable_authenticator_manager: true + + access_decision_manager: + strategy: unanimous + allow_if_all_abstain: true + + # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords + password_hashers: + Sulu\Bundle\SecurityBundle\Entity\User: bcrypt + + # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider + providers: + sulu: + id: sulu_security.user_provider + + firewalls: + website: + pattern: ^/ + lazy: true + provider: sulu + # The login and logout routes need to be created. + # For an advanced user management with registration and opt-in emails have a look at the: + # https://github.com/sulu/SuluCommunityBundle + # Also have a look at the user context based caching when you output user role specific data + # https://docs.sulu.io/en/2.2/cookbook/user-context-caching.html + form_login: + login_path: login + check_path: login + logout: + path: logout + target: / + remember_me: + secret: "%kernel.secret%" + lifetime: 604800 # 1 week in seconds + path: / + +sulu_security: + checker: + enabled: true diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/routing_website.yml b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/routing_website.yml index 8c053f59c49..fb901b9d640 100644 --- a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/routing_website.yml +++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/routing_website.yml @@ -15,3 +15,9 @@ sulu_media: _portal_loader_test: resource: "routing_portal_loader_test.yml" type: portal + +login: + path: /login + controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController + defaults: + template: login.html.twig diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/webspaces/sulu_lo.xml b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/webspaces/sulu_lo.xml index 5655035edb4..e3eccee0adf 100644 --- a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/webspaces/sulu_lo.xml +++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/webspaces/sulu_lo.xml @@ -89,4 +89,3 @@ - diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Functional/EventListener/SecurityListenerTest.php b/src/Sulu/Bundle/WebsiteBundle/Tests/Functional/EventListener/SecurityListenerTest.php new file mode 100644 index 00000000000..68d9b49fff1 --- /dev/null +++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Functional/EventListener/SecurityListenerTest.php @@ -0,0 +1,152 @@ +markTestSkipped('This test is only for Symfony 5.0 and above'); + } + + $this->client = $this->createWebsiteClient(['environment' => 'test_with_security']); + $this->purgeDatabase(); + $this->initPhpcr(); + } + + public function testNoPermissions(): void + { + $pageDocument = $this->createSecuredPage(); + + $this->client->request('GET', 'http://sulu.lo/'); + $response = $this->client->getResponse(); + + $this->assertSame(200, $response->getStatusCode()); + $this->assertNull($response->headers->get('Location')); + } + + public function testRedirectToLoginWhenNoAccess(): void + { + $pageDocument = $this->createSecuredPage(); + + $this->client->request('GET', 'http://sulu.lo/secure-area'); + $response = $this->client->getResponse(); + + $this->assertSame(302, $response->getStatusCode()); + $this->assertSame('http://sulu.lo/login', $response->headers->get('Location')); + } + + private function createSecuredPage(): BasePageDocument + { + $documentManager = $this->getContainer()->get('sulu_document_manager.document_manager'); + $entityManager = $this->getEntityManager(); + + $pageData = [ + 'locale' => 'en', + 'title' => 'Secure Area', + 'url' => '/secure-area', + 'article' => '

Some sample text for this super secret area.

', + 'structureType' => 'default', + ]; + + $extensionData = [ + 'seo' => [], + 'excerpt' => [], + ]; + + /** @var PageDocument $pageDocument */ + $pageDocument = $documentManager->create('page'); + + $pageDocument->setNavigationContexts([]); + $pageDocument->setLocale($pageData['locale']); + $pageDocument->setTitle($pageData['title']); + $pageDocument->setResourceSegment($pageData['url']); + $pageDocument->setStructureType($pageData['structureType']); + $pageDocument->setWorkflowStage(WorkflowStage::PUBLISHED); + $pageDocument->getStructure()->bind($pageData); + $pageDocument->setAuthor(1); + $pageDocument->setExtensionsData($extensionData); + $pageDocument->setPermissions([ + 1 => [ // do not allow anonymous users to access this page + 'view' => false, + ], + ]); + + $documentManager->persist( + $pageDocument, + 'en', + ['parent_path' => '/cmf/sulu_io/contents'] + ); + $documentManager->flush(); + + // We need to add access control here as we do not have the document id before + $role = new Role(); + $role->setName('Anonymous User Website'); + $role->setSystem('sulu_io'); + $role->setAnonymous(true); + + $permission = new Permission(); + $permission->setRole($role); + $permission->setPermissions(127); + $permission->setContext('sulu.webspaces.sulu_io'); + $role->addPermission($permission); + + $accessControl = new AccessControl(); + $accessControl->setPermissions(0); + $accessControl->setEntityId($pageDocument->getUuid()); + $accessControl->setEntityClass(SecurityBehavior::class); + $accessControl->setRole($role); + + $entityManager->persist($permission); + $entityManager->persist($role); + $entityManager->persist($accessControl); + $entityManager->flush(); + + $pageDocument->setPermissions([ + $role->getId() => [ // do not allow anonymous users to access this page + 'view' => false, + ], + ]); + $documentManager->persist( + $pageDocument, + 'en', + ['parent_path' => '/cmf/sulu_io/contents'] + ); + $documentManager->flush(); + $documentManager->publish($pageDocument, 'en'); + + $documentManager->clear(); + $entityManager->clear(); + + return $pageDocument; + } +} diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Unit/Routing/ContentRouteProviderTest.php b/src/Sulu/Bundle/WebsiteBundle/Tests/Unit/Routing/ContentRouteProviderTest.php index f55d26f95bd..1b1b4181241 100644 --- a/src/Sulu/Bundle/WebsiteBundle/Tests/Unit/Routing/ContentRouteProviderTest.php +++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Unit/Routing/ContentRouteProviderTest.php @@ -35,15 +35,12 @@ use Sulu\Component\DocumentManager\DocumentManagerInterface; use Sulu\Component\DocumentManager\Metadata; use Sulu\Component\Localization\Localization; -use Sulu\Component\Security\Authorization\PermissionTypes; use Sulu\Component\Security\Authorization\SecurityCheckerInterface; -use Sulu\Component\Security\Authorization\SecurityCondition; use Sulu\Component\Webspace\Analyzer\Attributes\RequestAttributes; use Sulu\Component\Webspace\Analyzer\RequestAnalyzer; use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface; use Sulu\Component\Webspace\Manager\WebspaceManagerInterface; use Sulu\Component\Webspace\Portal; -use Sulu\Component\Webspace\Security; use Sulu\Component\Webspace\Segment; use Sulu\Component\Webspace\Webspace; use Symfony\Component\HttpFoundation\Request; @@ -184,161 +181,6 @@ public function testGetCollectionForRequest(): void $this->assertEquals(false, $defaults['partial']); } - public function testSecurityChecker(): void - { - $attributes = $this->prophesize(RequestAttributes::class); - - $attributes->getAttribute('localization', null)->willReturn(new Localization('de')); - - $portal = new Portal(); - $portal->setKey('portal'); - $webspace = new Webspace(); - $webspace->setKey('webspace'); - $webspace->setTheme('theme'); - $security = new Security(); - $security->setSystem('website'); - $security->setPermissionCheck(true); - $webspace->setSecurity($security); - $portal->setWebspace($webspace); - $attributes->getAttribute('portal', null)->willReturn($portal); - - $attributes->getAttribute('matchType', null)->willReturn(RequestAnalyzer::MATCH_TYPE_FULL); - $attributes->getAttribute('resourceLocator', null)->willReturn(null); - $attributes->getAttribute('resourceLocatorPrefix', null)->willReturn('/de'); - - $this->resourceLocatorStrategy->loadByResourceLocator('', 'webspace', 'de')->willReturn('some-uuid'); - - $document = $this->prophesize(TitleBehavior::class) - ->willImplement(ExtensionBehavior::class) - ->willImplement(RedirectTypeBehavior::class) - ->willImplement(StructureBehavior::class) - ->willImplement(WebspaceBehavior::class) - ->willImplement(UuidBehavior::class); - $document->getUuid()->willReturn('some-uuid'); - $document->getTitle()->willReturn('some-title'); - $document->getWebspaceName()->willReturn('webspace'); - $document->getLocale()->willReturn('de'); - $document->getRedirectType()->willReturn(RedirectType::NONE); - $document->getStructureType()->willReturn('default'); - $document->getUuid()->willReturn('some-uuid'); - $document->getExtensionsData()->willReturn(['excerpt' => ['segments' => null]]); - $this->documentManager->find('some-uuid', 'de', ['load_ghost_content' => false])->willReturn($document->reveal()); - - $metadata = new Metadata(); - $metadata->setAlias('page'); - $structureMetadata = new StructureMetadata(); - $this->documentInspector->getMetadata($document->reveal())->willReturn($metadata); - $this->documentInspector->getStructureMetadata($document->reveal())->willReturn($structureMetadata); - - $pageBridge = $this->prophesize(PageBridge::class); - $pageBridge->getController()->willReturn('::Controller'); - $this->structureManager->wrapStructure('page', $structureMetadata)->willReturn($pageBridge->reveal()); - - $request = new Request( - [], - [], - ['_sulu' => $attributes->reveal()], - [], - [], - ['REQUEST_URI' => \rawurlencode('/de')] - ); - - $pageBridge->setDocument($document->reveal())->shouldBeCalled(); - - $securityChecker = $this->prophesize(SecurityCheckerInterface::class); - $securityChecker->checkPermission(Argument::that(function(SecurityCondition $securityCondition) use ($document) { - $this->assertSame('some-uuid', $securityCondition->getObjectId()); - $this->assertSame(\get_class($document->reveal()), $securityCondition->getObjectType()); - $this->assertSame('de', $securityCondition->getLocale()); - $this->assertSame('sulu.webspaces.webspace', $securityCondition->getSecurityContext()); - - return true; - }), PermissionTypes::VIEW)->shouldBeCalled(); - - $contentRouteProvider = $this->createContentRouteProvider($securityChecker->reveal()); - $routes = $contentRouteProvider->getRouteCollectionForRequest($request); - - $defaults = $routes->getIterator()->current()->getDefaults(); - - $this->assertCount(1, $routes); - $this->assertEquals($pageBridge->reveal(), $defaults['structure']); - $this->assertEquals(false, $defaults['partial']); - } - - public function testSecurityCheckerWithoutPermissionCheck(): void - { - $attributes = $this->prophesize(RequestAttributes::class); - - $attributes->getAttribute('localization', null)->willReturn(new Localization('de')); - - $portal = new Portal(); - $portal->setKey('portal'); - $webspace = new Webspace(); - $webspace->setKey('webspace'); - $webspace->setTheme('theme'); - $security = new Security(); - $security->setSystem('website'); - $security->setPermissionCheck(false); - $webspace->setSecurity($security); - $portal->setWebspace($webspace); - $attributes->getAttribute('portal', null)->willReturn($portal); - - $attributes->getAttribute('matchType', null)->willReturn(RequestAnalyzer::MATCH_TYPE_FULL); - $attributes->getAttribute('resourceLocator', null)->willReturn(null); - $attributes->getAttribute('resourceLocatorPrefix', null)->willReturn('/de'); - - $this->resourceLocatorStrategy->loadByResourceLocator('', 'webspace', 'de')->willReturn('some-uuid'); - - $document = $this->prophesize(TitleBehavior::class) - ->willImplement(ExtensionBehavior::class) - ->willImplement(RedirectTypeBehavior::class) - ->willImplement(StructureBehavior::class) - ->willImplement(WebspaceBehavior::class) - ->willImplement(UuidBehavior::class); - $document->getUuid()->willReturn('some-uuid'); - $document->getTitle()->willReturn('some-title'); - $document->getWebspaceName()->willReturn('webspace'); - $document->getLocale()->willReturn('de'); - $document->getRedirectType()->willReturn(RedirectType::NONE); - $document->getStructureType()->willReturn('default'); - $document->getUuid()->willReturn('some-uuid'); - $document->getExtensionsData()->willReturn(['excerpt' => ['segments' => null]]); - $this->documentManager->find('some-uuid', 'de', ['load_ghost_content' => false])->willReturn($document->reveal()); - - $metadata = new Metadata(); - $metadata->setAlias('page'); - $structureMetadata = new StructureMetadata(); - $this->documentInspector->getMetadata($document->reveal())->willReturn($metadata); - $this->documentInspector->getStructureMetadata($document->reveal())->willReturn($structureMetadata); - - $pageBridge = $this->prophesize(PageBridge::class); - $pageBridge->getController()->willReturn('::Controller'); - $this->structureManager->wrapStructure('page', $structureMetadata)->willReturn($pageBridge->reveal()); - - $request = new Request( - [], - [], - ['_sulu' => $attributes->reveal()], - [], - [], - ['REQUEST_URI' => \rawurlencode('/de')] - ); - - $pageBridge->setDocument($document->reveal())->shouldBeCalled(); - - $securityChecker = $this->prophesize(SecurityCheckerInterface::class); - $securityChecker->checkPermission(Argument::cetera())->shouldNotBeCalled(); - - $contentRouteProvider = $this->createContentRouteProvider($securityChecker->reveal()); - $routes = $contentRouteProvider->getRouteCollectionForRequest($request); - - $defaults = $routes->getIterator()->current()->getDefaults(); - - $this->assertCount(1, $routes); - $this->assertEquals($pageBridge->reveal(), $defaults['structure']); - $this->assertEquals(false, $defaults['partial']); - } - public function testGetCollectionForRequestWithWrongSegment(): void { $attributes = $this->prophesize(RequestAttributes::class); diff --git a/src/Sulu/Component/Webspace/Analyzer/Attributes/SystemRequestProcessor.php b/src/Sulu/Component/Webspace/Analyzer/Attributes/SystemRequestProcessor.php new file mode 100644 index 00000000000..1a289f6e78a --- /dev/null +++ b/src/Sulu/Component/Webspace/Analyzer/Attributes/SystemRequestProcessor.php @@ -0,0 +1,76 @@ +systemStore = $systemStore; + $this->context = $context; + } + + public function process(Request $request, RequestAttributes $requestAttributes) + { + $attributes = []; + if (SuluKernel::CONTEXT_ADMIN === $this->context) { + $this->systemStore->setSystem(Admin::SULU_ADMIN_SECURITY_SYSTEM); + $attributes['system'] = Admin::SULU_ADMIN_SECURITY_SYSTEM; + + return new RequestAttributes($attributes); + } + $portalInformation = $requestAttributes->getAttribute('portalInformation'); + + if (!$portalInformation instanceof PortalInformation) { + return new RequestAttributes($attributes); + } + + $webspace = $portalInformation->getWebspace(); + + $security = $webspace->getSecurity(); + if ($security) { + $attributes['system'] = $security->getSystem(); + $this->systemStore->setSystem($attributes['system']); + } + + return new RequestAttributes($attributes); + } + + public function validate(RequestAttributes $attributes) + { + return true; + } +}