From 50e45bfd25da10d3115d5a0333ccb9978d629f8f Mon Sep 17 00:00:00 2001 From: "lenaick.moreira" Date: Mon, 18 May 2026 15:32:47 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B09614=20-=20Add=20a=20specific=20route=20?= =?UTF-8?q?to=20display=20the=20icons=20for=20an=20EventNotificationNewsro?= =?UTF-8?q?om?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ormdocument.class.inc.php | 5 +- .../Newsroom/iTopNewsroomController.php | 47 ++++++++++++++++++- .../unitary-tests/core/ormDocumentTest.php | 15 ++++++ 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/core/ormdocument.class.inc.php b/core/ormdocument.class.inc.php index 7e7b2bb20b..0f9b4312ba 100644 --- a/core/ormdocument.class.inc.php +++ b/core/ormdocument.class.inc.php @@ -340,13 +340,14 @@ class ormDocument * @param string $sContentDisposition Either 'inline' or 'attachment' * @param string $sSecretField The attcode of the field containing a "secret" to be provided in order to retrieve the file * @param string $sSecretValue The value of the secret to be compared with the value of the attribute $sSecretField + * @param bool $bAllowAllData If true, no rights filtering is applied * * @return void */ - public static function DownloadDocument(WebPage $oPage, $sClass, $id, $sAttCode, $sContentDisposition = 'attachment', $sSecretField = null, $sSecretValue = null) + public static function DownloadDocument(WebPage $oPage, $sClass, $id, $sAttCode, $sContentDisposition = 'attachment', $sSecretField = null, $sSecretValue = null, $bAllowAllData = false) { try { - $oObj = MetaModel::GetObject($sClass, $id, false, false); + $oObj = MetaModel::GetObject($sClass, $id, false, $bAllowAllData); if (!is_object($oObj)) { // If access to the document is not granted, check if the access to the host object is allowed $oObj = MetaModel::GetObject($sClass, $id, false, true); diff --git a/sources/Controller/Newsroom/iTopNewsroomController.php b/sources/Controller/Newsroom/iTopNewsroomController.php index 37ea7bebfd..d517b07d17 100644 --- a/sources/Controller/Newsroom/iTopNewsroomController.php +++ b/sources/Controller/Newsroom/iTopNewsroomController.php @@ -16,6 +16,7 @@ use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory; use Combodo\iTop\Application\UI\Base\Layout\Object\ObjectSummary; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock; +use Combodo\iTop\Application\WebPage\DownloadPage; use Combodo\iTop\Application\WebPage\iTopWebPage; use Combodo\iTop\Application\WebPage\JsonPage; use Combodo\iTop\Application\WebPage\JsonPPage; @@ -28,6 +29,7 @@ use DBObjectSet; use Dict; use EventNotificationNewsroom; use MetaModel; +use ormDocument; use SecurityException; use UserRights; use utils; @@ -376,9 +378,10 @@ JS $oEventBlock->SetCSSColorClass($sReadColor); $oEventBlock->SetSubTitle($sReadLabel); $oEventBlock->SetClassLabel(''); + /** @var \ormDocument $oImage */ $oImage = $oEvent->Get('icon'); if (!$oImage->IsEmpty()) { - $sIconUrl = $oImage->GetDisplayURL(get_class($oEvent), $iEventId, 'icon'); + $sIconUrl = self::GetDisplayIconUrl($iEventId, $oImage->GetSignature()); $oEventBlock->SetIcon($sIconUrl, Panel::ENUM_ICON_COVER_METHOD_COVER, true); } @@ -542,7 +545,7 @@ $sMessage HTML; $sIcon = $oMessage->Get('icon') !== null ? - $oMessage->Get('icon')->GetDisplayURL(EventNotificationNewsroom::class, $oMessage->GetKey(), 'icon') : + $this->GetDisplayIconUrl($oMessage->GetKey(), $oMessage->Get('icon')->GetSignature()) : Branding::GetCompactMainLogoAbsoluteUrl(); $aMessages[] = [ 'id' => $oMessage->GetKey(), @@ -689,6 +692,41 @@ HTML; return $oPage; } + /** + * Display the icon of an EventNotificationNewsroom + * (copy of ajax.render.php?operation=display_document but with the bAllowAllData parameter set to true in order to bypass the data access restrictions since the icon is not a critical information) + * @return void + * @throws \ConfigException + * @throws \CoreException + */ + public function OperationViewIcon(): void + { + $sId = utils::ReadParam('id', ''); + if (!empty($sId)) { + $oPage = new DownloadPage(''); + // X-Frame http header : set in page constructor, but we need to allow frame integration for this specific page + // so we're resetting its value ! (see N°3416) + $oPage->add_xframe_options(''); + $iCacheSec = (int)utils::ReadParam('cache', 0); + $oPage->set_cache($iCacheSec); + + // N°4129 - Prevent XSS attacks & other script executions + if (utils::GetConfig()->Get('security.disable_inline_documents_sandbox') === false) { + $oPage->add_header('Content-Security-Policy: sandbox;'); + } + + ormDocument::DownloadDocument( + $oPage, + EventNotificationNewsroom::class, + $sId, + 'icon', + ormDocument::ENUM_CONTENT_DISPOSITION_INLINE, + bAllowAllData: true + ); + $oPage->output(); + } + } + /** * @param string $sAction * @@ -781,4 +819,9 @@ HTML; return $aReturnData; } + + protected function GetDisplayIconUrl(string $sId, string $sSignature): string + { + return utils::GetAbsoluteUrlAppRoot()."pages/UI.php?route=itopnewsroom.view_icon&id=$sId&s=$sSignature&cache=86400"; + } } diff --git a/tests/php-unit-tests/unitary-tests/core/ormDocumentTest.php b/tests/php-unit-tests/unitary-tests/core/ormDocumentTest.php index b9e7a090c4..6303e3a5d8 100644 --- a/tests/php-unit-tests/unitary-tests/core/ormDocumentTest.php +++ b/tests/php-unit-tests/unitary-tests/core/ormDocumentTest.php @@ -198,6 +198,21 @@ class ormDocumentTest extends ItopDataTestCase $this->assertStringNotContainsString('the object does not exist or you are not allowed to view it', $sAllowedHtml, 'Unexpected error message when rights are sufficient.'); } + /** + * @dataProvider DownloadDocumentRightsProvider + */ + public function testAllowsDownloadingDocumentWhenBypassingRightsChecksWithAllowAllData(string $sTargetClass, string $sAttCode, string $sData, string $sFileName, ?string $sHostClass) + { + $iDeniedDocumentId = $this->CreateDownloadTargetInOrg($sTargetClass, $sAttCode, $this->iOrgDifferentFromUser, $sData, $sFileName, $sHostClass); + + $oPageAllowed = new CaptureWebPage(); + ormDocument::DownloadDocument($oPageAllowed, $sTargetClass, $iDeniedDocumentId, $sAttCode, ormDocument::ENUM_CONTENT_DISPOSITION_INLINE, bAllowAllData: true); + $sAllowedHtml = $oPageAllowed->GetHtml(); + + $this->assertStringContainsString($sData, $sAllowedHtml, 'Expected file data present when bypassing rights checks.'); + $this->assertStringNotContainsString("Invalid id ($iDeniedDocumentId) for class '$sTargetClass' - the object does not exist or you are not allowed to view it", $sAllowedHtml, 'Unexpected invalid id error message when bypassing rights checks.'); + } + public function DownloadDocumentRightsProvider(): array { return [