From 5996cd2675b06745c34e0c62a84f2c445a807274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3js?= Date: Thu, 26 Oct 2017 14:36:03 +0200 Subject: [PATCH] Fix EZP-28123: Embedded content throws exception after it is moved to trash --- .../MVC/Symfony/FieldType/RichText/Renderer.php | 41 ++++- .../FieldType/Tests/RichText/RendererTest.php | 171 +++++++++++++++++---- 2 files changed, 181 insertions(+), 31 deletions(-) diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/RichText/Renderer.php b/eZ/Publish/Core/MVC/Symfony/FieldType/RichText/Renderer.php index 95743d4f24..4c8d525ed3 100644 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/RichText/Renderer.php +++ b/eZ/Publish/Core/MVC/Symfony/FieldType/RichText/Renderer.php @@ -9,6 +9,7 @@ namespace eZ\Publish\Core\MVC\Symfony\FieldType\RichText; use eZ\Publish\API\Repository\Repository; +use eZ\Publish\API\Repository\Values\Content\Content; use eZ\Publish\API\Repository\Values\Content\VersionInfo; use eZ\Publish\Core\FieldType\RichText\RendererInterface; use eZ\Publish\Core\MVC\ConfigResolverInterface; @@ -123,7 +124,24 @@ public function renderContentEmbed($contentId, $viewType, array $parameters, $is $isDenied = false; try { - $this->checkContent($contentId); + /** @var \eZ\Publish\API\Repository\Values\Content\Content $content */ + $content = $this->repository->sudo( + function (Repository $repository) use ($contentId) { + return $repository->getContentService()->loadContent($contentId); + } + ); + + if (!$content->contentInfo->mainLocationId) { + if (isset($this->logger)) { + $this->logger->error( + "Could not render embedded resource: Content #{$contentId} is trashed." + ); + } + + return null; + } + + $this->checkContentPermissions($content); } catch (AccessDeniedException $e) { if (isset($this->logger)) { $this->logger->error( @@ -366,17 +384,30 @@ protected function getEmbedTemplateName($resourceType, $isInline, $isDenied) * * @throws \Symfony\Component\Security\Core\Exception\AccessDeniedException * - * @param int|string $id + * @deprecated since 6.7 + * @param int $contentId */ - protected function checkContent($id) + protected function checkContent($contentId) { /** @var \eZ\Publish\API\Repository\Values\Content\Content $content */ $content = $this->repository->sudo( - function (Repository $repository) use ($id) { - return $repository->getContentService()->loadContent($id); + function (Repository $repository) use ($contentId) { + return $repository->getContentService()->loadContent($contentId); } ); + $this->checkContentPermissions($content); + } + + /** + * Check embed permissions for the given Content. + * + * @throws \Symfony\Component\Security\Core\Exception\AccessDeniedException + * + * @param \eZ\Publish\API\Repository\Values\Content\Content $content + */ + protected function checkContentPermissions(Content $content) + { // Check both 'content/read' and 'content/view_embed'. if ( !$this->authorizationChecker->isGranted( diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/RichText/RendererTest.php b/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/RichText/RendererTest.php index 5d85fe9992..b3b0a688d2 100644 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/RichText/RendererTest.php +++ b/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/RichText/RendererTest.php @@ -8,6 +8,8 @@ */ namespace eZ\Publish\Core\MVC\Symfony\FieldType\Tests\RichText; +use eZ\Publish\API\Repository\Values\Content\Content; +use eZ\Publish\API\Repository\Values\Content\ContentInfo; use eZ\Publish\Core\MVC\Symfony\FieldType\RichText\Renderer; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use eZ\Publish\Core\Base\Exceptions\NotFoundException; @@ -306,7 +308,7 @@ public function testRenderTagWithTemplate( public function testRenderContentEmbed() { - $renderer = $this->getMockedRenderer(array('render', 'checkContent', 'getEmbedTemplateName')); + $renderer = $this->getMockedRenderer(array('render', 'checkContentPermissions', 'getEmbedTemplateName')); $contentId = 42; $viewType = 'embedTest'; $templateName = 'templateName'; @@ -314,11 +316,20 @@ public function testRenderContentEmbed() $isInline = true; $isDenied = false; $result = 'result'; + $mainLocationId = 2; + + $contentMock = $this->getContentMock($mainLocationId); + + $this->repositoryMock + ->expects($this->once()) + ->method('sudo') + ->willReturn($contentMock); $renderer ->expects($this->once()) - ->method('checkContent') - ->with($contentId); + ->method('checkContentPermissions') + ->with($contentMock) + ->willReturn($contentMock); $renderer ->expects($this->once()) @@ -350,18 +361,27 @@ public function testRenderContentEmbed() public function testRenderContentEmbedNoTemplateConfigured() { - $renderer = $this->getMockedRenderer(array('render', 'checkContent', 'getEmbedTemplateName')); + $renderer = $this->getMockedRenderer(array('render', 'checkContentPermissions', 'getEmbedTemplateName')); $contentId = 42; $viewType = 'embedTest'; $templateName = null; $parameters = array('parameters'); $isInline = true; $isDenied = false; + $mainLocationId = 2; + + $contentMock = $this->getContentMock($mainLocationId); + + $this->repositoryMock + ->expects($this->once()) + ->method('sudo') + ->willReturn($contentMock); $renderer ->expects($this->once()) - ->method('checkContent') - ->with($contentId); + ->method('checkContentPermissions') + ->with($contentMock) + ->willReturn($contentMock); $renderer ->expects($this->never()) @@ -390,18 +410,26 @@ public function testRenderContentEmbedNoTemplateConfigured() public function testRenderContentEmbedNoTemplateFound() { - $renderer = $this->getMockedRenderer(array('render', 'checkContent', 'getEmbedTemplateName')); + $renderer = $this->getMockedRenderer(array('render', 'checkContentPermissions', 'getEmbedTemplateName')); $contentId = 42; $viewType = 'embedTest'; $templateName = 'templateName'; $parameters = array('parameters'); $isInline = true; $isDenied = false; + $mainLocationId = 2; + + $contentMock = $this->getContentMock($mainLocationId); + + $this->repositoryMock + ->expects($this->once()) + ->method('sudo') + ->willReturn($contentMock); $renderer ->expects($this->once()) - ->method('checkContent') - ->with($contentId); + ->method('checkContentPermissions') + ->with($contentMock); $renderer ->expects($this->never()) @@ -432,7 +460,7 @@ public function testRenderContentEmbedNoTemplateFound() public function testRenderContentEmbedAccessDenied() { - $renderer = $this->getMockedRenderer(array('render', 'checkContent', 'getEmbedTemplateName')); + $renderer = $this->getMockedRenderer(array('render', 'checkContentPermissions', 'getEmbedTemplateName')); $contentId = 42; $viewType = 'embedTest'; $templateName = 'templateName'; @@ -440,11 +468,19 @@ public function testRenderContentEmbedAccessDenied() $isInline = true; $isDenied = true; $result = 'result'; + $mainLocationId = 2; + + $contentMock = $this->getContentMock($mainLocationId); + + $this->repositoryMock + ->expects($this->once()) + ->method('sudo') + ->willReturn($contentMock); $renderer ->expects($this->once()) - ->method('checkContent') - ->with($contentId) + ->method('checkContentPermissions') + ->with($contentMock) ->will($this->throwException(new AccessDeniedException())); $renderer @@ -476,6 +512,44 @@ public function testRenderContentEmbedAccessDenied() ); } + public function testRenderContentEmbedTrashed() + { + $renderer = $this->getMockedRenderer(['checkContentPermissions']); + $contentId = 42; + $viewType = 'embedTest'; + $parameters = array('parameters'); + $isInline = true; + + $contentInfoMock = $this->getMock(ContentInfo::class); + $contentInfoMock + ->expects($this->once()) + ->method('__get') + ->with('mainLocationId') + ->willReturn(null); + + $contentMock = $this->getMock(Content::class); + $contentMock + ->expects($this->once()) + ->method('__get') + ->with('contentInfo') + ->willReturn($contentInfoMock); + + $this->repositoryMock + ->expects($this->once()) + ->method('sudo') + ->willReturn($contentMock); + + $this->loggerMock + ->expects($this->once()) + ->method('error') + ->with("Could not render embedded resource: Content #{$contentId} is trashed."); + + $this->assertEquals( + null, + $renderer->renderContentEmbed($contentId, $viewType, $parameters, $isInline) + ); + } + public function providerForTestRenderContentEmbedNotFound() { return array( @@ -489,17 +563,25 @@ public function providerForTestRenderContentEmbedNotFound() */ public function testRenderContentEmbedNotFound(Exception $exception) { - $renderer = $this->getMockedRenderer(array('render', 'checkContent', 'getEmbedTemplateName')); + $renderer = $this->getMockedRenderer(array('render', 'checkContentPermissions', 'getEmbedTemplateName')); $contentId = 42; $viewType = 'embedTest'; $parameters = array('parameters'); $isInline = true; $result = null; + $mainLocationId = 2; + + $contentMock = $this->getContentMock($mainLocationId); + + $this->repositoryMock + ->expects($this->once()) + ->method('sudo') + ->willReturn($contentMock); $renderer ->expects($this->once()) - ->method('checkContent') - ->with($contentId) + ->method('checkContentPermissions') + ->with($contentMock) ->will($this->throwException($exception)); $renderer @@ -531,13 +613,21 @@ public function testRenderContentEmbedNotFound(Exception $exception) */ public function testRenderContentEmbedThrowsException() { - $renderer = $this->getMockedRenderer(array('checkContent')); + $renderer = $this->getMockedRenderer(array('checkContentPermissions')); $contentId = 42; + $mainLocationId = 2; + + $contentMock = $this->getContentMock($mainLocationId); + + $this->repositoryMock + ->expects($this->once()) + ->method('sudo') + ->willReturn($contentMock); $renderer ->expects($this->once()) - ->method('checkContent') - ->with($contentId) + ->method('checkContentPermissions') + ->with($contentMock) ->will($this->throwException(new Exception('Something threw up'))); $renderer->renderContentEmbed($contentId, 'embedTest', array('parameters'), true); @@ -675,22 +765,31 @@ public function testRenderContentWithTemplate( $renderTemplate, $renderResult ) { - $renderer = $this->getMockedRenderer(array('render', 'checkContent')); + $renderer = $this->getMockedRenderer(array('render', 'checkContentPermissions')); $contentId = 42; $viewType = 'embedTest'; $parameters = array('parameters'); + $mainLocationId = 2; + + $contentMock = $this->getContentMock($mainLocationId); + + $this->repositoryMock + ->expects($this->once()) + ->method('sudo') + ->willReturn($contentMock); if (isset($deniedException)) { $renderer ->expects($this->once()) - ->method('checkContent') - ->with($contentId) + ->method('checkContentPermissions') + ->with($contentMock) ->will($this->throwException($deniedException)); } else { $renderer ->expects($this->once()) - ->method('checkContent') - ->with($contentId); + ->method('checkContentPermissions') + ->with($contentMock) + ->willReturn($contentMock); } if (!isset($renderTemplate)) { @@ -1322,9 +1421,10 @@ protected function getMockedRenderer(array $methods = array()) */ protected function getRepositoryMock() { - return $this->getMock( - 'eZ\\Publish\\API\\Repository\\Repository' - ); + return $this + ->getMockBuilder('eZ\\Publish\\Core\\Repository\\Repository') + ->disableOriginalConstructor() + ->getMock(); } /** @@ -1386,4 +1486,23 @@ protected function getLoggerMock() 'Psr\\Log\\LoggerInterface' ); } + + protected function getContentMock($mainLocationId) + { + $contentInfoMock = $this->getMock(ContentInfo::class); + $contentInfoMock + ->expects($this->once()) + ->method('__get') + ->with('mainLocationId') + ->willReturn($mainLocationId); + + $contentMock = $this->getMock(Content::class); + $contentMock + ->expects($this->once()) + ->method('__get') + ->with('contentInfo') + ->willReturn($contentInfoMock); + + return $contentMock; + } }