From c98ad106c417c950169b0a7b8c00950df7861ea8 Mon Sep 17 00:00:00 2001 From: odain Date: Thu, 1 Jul 2021 17:34:23 +0200 Subject: [PATCH 01/16] =?UTF-8?q?N=C2=B04125=20-=20Make=20translations=20l?= =?UTF-8?q?oading=20more=20robust=20toward=20APCu=20cache=20corruption=20o?= =?UTF-8?q?r=20invalid=20dictionnary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/apc-service.class.inc.php | 30 +++ core/dict.class.inc.php | 51 +++-- test/core/dictApcuTest.php | 390 +++++++++++++++++++++++++++++++++ test/core/dictTest.php | 64 +++++- 4 files changed, 520 insertions(+), 15 deletions(-) create mode 100644 core/apc-service.class.inc.php create mode 100644 test/core/dictApcuTest.php diff --git a/core/apc-service.class.inc.php b/core/apc-service.class.inc.php new file mode 100644 index 000000000..6d367e8fa --- /dev/null +++ b/core/apc-service.class.inc.php @@ -0,0 +1,30 @@ +function_exists($function_name); + } + + /** + * @param $key string|array + * @return mixed + */ + function apc_fetch($key) + { + return apc_fetch($key); + } + + /** + * @param array|string $key + * @param $var + * @param int $ttl + * @return array|bool + */ + function apc_store($key, $var = NULL, $ttl = 0) + { + return apc_store($key, $var, $ttl); + } +} \ No newline at end of file diff --git a/core/dict.class.inc.php b/core/dict.class.inc.php index a84b04779..df644ce84 100644 --- a/core/dict.class.inc.php +++ b/core/dict.class.inc.php @@ -64,6 +64,8 @@ class Dict protected static $m_aLanguages = array(); // array( code => array( 'description' => '...', 'localized_description' => '...') ...) protected static $m_aData = array(); protected static $m_sApplicationPrefix = null; + /** @var \ApcService $m_oApcService */ + protected static $m_oApcService = null; /** * @param $sLanguageCode @@ -145,15 +147,17 @@ class Dict { // Attempt to find the string in the user language // - self::InitLangIfNeeded(self::GetUserLanguage()); + $sLangCode = self::GetUserLanguage(); + self::InitLangIfNeeded($sLangCode); - if (!array_key_exists(self::GetUserLanguage(), self::$m_aData)) + if (!array_key_exists($sLangCode, self::$m_aData)) { + IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed"); // It may happen, when something happens before the dictionaries get loaded return $sStringCode; } - $aCurrentDictionary = self::$m_aData[self::GetUserLanguage()]; - if (array_key_exists($sStringCode, $aCurrentDictionary)) + $aCurrentDictionary = self::$m_aData[$sLangCode]; + if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary)) { return $aCurrentDictionary[$sStringCode]; } @@ -164,7 +168,7 @@ class Dict self::InitLangIfNeeded(self::$m_sDefaultLanguage); $aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage]; - if (array_key_exists($sStringCode, $aDefaultDictionary)) + if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary)) { return $aDefaultDictionary[$sStringCode]; } @@ -173,7 +177,7 @@ class Dict self::InitLangIfNeeded('EN US'); $aDefaultDictionary = self::$m_aData['EN US']; - if (array_key_exists($sStringCode, $aDefaultDictionary)) + if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary)) { return $aDefaultDictionary[$sStringCode]; } @@ -232,7 +236,24 @@ class Dict { self::$m_aLanguages = $aLanguagesList; } - + + /** + * @return \ApcService + */ + public static function GetApcService() { + if (self::$m_oApcService === null){ + self::$m_oApcService = new ApcService(); + } + return self::$m_oApcService; + } + + /** + * @param \ApcService $m_oApcService + */ + public static function SetApcService($oApcService) { + self::$m_oApcService = $oApcService; + } + /** * Load a language from the language dictionary, if not already loaded * @param string $sLangCode Language code @@ -241,15 +262,16 @@ class Dict public static function InitLangIfNeeded($sLangCode) { if (array_key_exists($sLangCode, self::$m_aData)) return true; - + $bResult = false; - - if (function_exists('apc_fetch') && (self::$m_sApplicationPrefix !== null)) + + if (self::GetApcService()->function_exists('apc_fetch') + && (self::$m_sApplicationPrefix !== null)) { // Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter // - self::$m_aData[$sLangCode] = apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode); - if (self::$m_aData[$sLangCode] === false) + self::$m_aData[$sLangCode] = self::GetApcService()->apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode); + if (! is_array(self::$m_aData[$sLangCode])) { unset(self::$m_aData[$sLangCode]); } @@ -263,9 +285,10 @@ class Dict $sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php'; require_once($sDictFile); - if (function_exists('apc_store') && (self::$m_sApplicationPrefix !== null)) + if (self::GetApcService()->function_exists('apc_store') + && (self::$m_sApplicationPrefix !== null)) { - apc_store(self::$m_sApplicationPrefix.'-dict-'.$sLangCode, self::$m_aData[$sLangCode]); + self::GetApcService()->apc_store(self::$m_sApplicationPrefix.'-dict-'.$sLangCode, self::$m_aData[$sLangCode]); } $bResult = true; } diff --git a/test/core/dictApcuTest.php b/test/core/dictApcuTest.php new file mode 100644 index 000000000..96ace5b7f --- /dev/null +++ b/test/core/dictApcuTest.php @@ -0,0 +1,390 @@ + +// + +/** + * Created by PhpStorm. + * User: Eric + * Date: 30/10/2017 + * Time: 13:43 + */ + +namespace Combodo\iTop\Test\UnitTest\Core; + +use Combodo\iTop\Test\UnitTest\ItopTestCase; +use Dict; +use Exception; + + +/** + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + * @backupGlobals disabled + */ +class dictApcuTest extends ItopTestCase +{ + private $sEnvName; + private $oApcService; + private $sDictionaryFolder; + + protected function setUp() + { + parent::setUp(); + require_once (APPROOT.'core/coreexception.class.inc.php'); + require_once (APPROOT.'core/dict.class.inc.php'); + require_once (APPROOT.'core/apc-service.class.inc.php'); + + $this->sEnvName = date("c"); + $_SESSION['itop_env'] = $this->sEnvName; + + $this->oApcService = $this->createMock(\ApcService::class); + Dict::SetApcService($this->oApcService); + Dict::EnableCache('toto'); + + Dict::SetLanguagesList(['FR FR' => 'fr', 'EN US' => 'en', 'DE DE' => 'de', 'RU RU' => 'de']); + + $this->InitDictionnaries(); + } + + private function InitDictionnaries(){ + clearstatcache(); + $this->sDictionaryFolder = APPROOT."env-$this->sEnvName/dictionaries"; + @mkdir($this->sDictionaryFolder, 0777, true); + + $sLabels = << 'fr1', +STR; + $this->InitDictionnary($this->sDictionaryFolder, 'FR FR', 'fr-fr', $sLabels); + + $sLabels = << 'ru1', + 'label2' => 'ru2', +STR; + $this->InitDictionnary($this->sDictionaryFolder, 'RU RU', 'ru-ru', $sLabels); + $sLabels = << 'en1', + 'label2' => 'en2', + 'label3' => 'en3', +STR; + $this->InitDictionnary($this->sDictionaryFolder, 'EN US', 'en-us', $sLabels); + + clearstatcache(); + } + + private function InitDictionnary($sDictionaryFolder, $sLanguageCode, $sLanguageCodeInFilename, $sLabels){ + $sContent = <<sEnvName/dictionaries/*") as $sFile){ + unlink($sFile); + } + rmdir(APPROOT."env-$this->sEnvName/dictionaries"); + rmdir(APPROOT."env-$this->sEnvName"); + } + + public function testInitLangIfNeeded_NoApc(){ + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(false); + + $this->oApcService->expects($this->never()) + ->method('apc_fetch'); + + $this->oApcService->expects($this->never()) + ->method('apc_store'); + + //EN US default language + $this->assertEquals('en1', Dict::S('label1')); + $this->assertEquals('en2', Dict::S('label2')); + $this->assertEquals('en3', Dict::S('label3')); + $this->assertEquals('not_defined_label', Dict::S('not_defined_label')); + + //default language set to RU RU + Dict::SetDefaultLanguage('RU RU'); + $this->assertEquals('ru1', Dict::S('label1')); + $this->assertEquals('ru2', Dict::S('label2')); + $this->assertEquals('en3', Dict::S('label3')); + $this->assertEquals('not_defined_label', Dict::S('not_defined_label')); + + //user language set to FR FR + Dict::SetUserLanguage('FR FR'); + $this->assertEquals('fr1', Dict::S('label1')); + $this->assertEquals('ru2', Dict::S('label2')); + $this->assertEquals('en3', Dict::S('label3')); + $this->assertEquals('not_defined_label', Dict::S('not_defined_label')); + } + + public function testInitLangIfNeeded_Apc_LanguageMismatchDictionnary(){ + //language mismatch!! + $sLabels = << 'de1', +STR; + $this->InitDictionnary($this->sDictionaryFolder, 'RU RU', 'de-de', $sLabels); + + clearstatcache(); + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(false); + + $this->oApcService->expects($this->exactly(0)) + ->method('apc_fetch'); + + $this->oApcService->expects($this->never()) + ->method('apc_store'); + + Dict::SetUserLanguage('DE DE'); + $this->assertEquals('label1', Dict::S('label1')); + } + + public function testInitLangIfNeeded_Apc_BrokenUserDictionnary(){ + $this->InitBrokenDictionnary($this->sDictionaryFolder, 'DE DE', 'de-de'); + + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(false); + + $this->oApcService->expects($this->exactly(0)) + ->method('apc_fetch'); + + $this->oApcService->expects($this->never()) + ->method('apc_store'); + + Dict::SetUserLanguage('DE DE'); + $this->assertEquals('en1', Dict::S('label1')); + + Dict::SetDefaultLanguage('RU RU'); + $this->assertEquals('ru1', Dict::S('label1')); + } + + public function testInitLangIfNeeded_Apc_BrokenDictionnary_UserAndDefault(){ + $this->InitBrokenDictionnary($this->sDictionaryFolder, 'DE DE', 'de-de'); + $this->InitBrokenDictionnary($this->sDictionaryFolder, 'RU RU', 'ru-ru'); + + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(false); + + $this->oApcService->expects($this->exactly(0)) + ->method('apc_fetch'); + + $this->oApcService->expects($this->never()) + ->method('apc_store'); + + Dict::SetUserLanguage('DE DE'); + Dict::SetDefaultLanguage('RU RU'); + $this->assertEquals('en1', Dict::S('label1')); + } + + public function testInitLangIfNeeded_Apc_BrokenDictionnary_ALL(){ + $this->InitBrokenDictionnary($this->sDictionaryFolder, 'DE DE', 'de-de'); + $this->InitBrokenDictionnary($this->sDictionaryFolder, 'RU RU', 'ru-ru'); + $this->InitBrokenDictionnary($this->sDictionaryFolder, 'EN US', 'en-us'); + + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(false); + + $this->oApcService->expects($this->exactly(0)) + ->method('apc_fetch'); + + $this->oApcService->expects($this->never()) + ->method('apc_store'); + + Dict::SetUserLanguage('DE DE'); + Dict::SetDefaultLanguage('RU RU'); + $this->assertEquals('label1', Dict::S('label1')); + } + + public function testInitLangIfNeeded_ApcFromCache_PropertyInUserDictionnary(){ + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(true); + + $this->oApcService->expects($this->exactly(1)) + ->method('apc_fetch') + ->with('toto-dict-FR FR') + ->willReturn(['label1' => 'fr1']); + + $this->oApcService->expects($this->exactly(0)) + ->method('apc_store'); + + Dict::SetDefaultLanguage('RU RU'); + Dict::SetUserLanguage('FR FR'); + $this->assertEquals('fr1', Dict::S('label1')); + } + + public function testInitLangIfNeeded_ApcStore_PropertyInUserDictionnary(){ + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(true); + + $this->oApcService->expects($this->exactly(1)) + ->method('apc_fetch') + ->with('toto-dict-FR FR') + ->willReturn(false); + + $this->oApcService->expects($this->exactly(1)) + ->method('apc_store') + ->with('toto-dict-FR FR', ['label1' => 'fr1']); + + Dict::SetDefaultLanguage('RU RU'); + Dict::SetUserLanguage('FR FR'); + $this->assertEquals('fr1', Dict::S('label1')); + } + + public function testInitLangIfNeeded_Apc_CorruptedCache_PropertyInUserDictionnary(){ + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(true); + + $this->oApcService->expects($this->exactly(1)) + ->method('apc_fetch') + ->with('toto-dict-FR FR') + ->willReturn('label1'); + + $this->oApcService->expects($this->exactly(1)) + ->method('apc_store') + ->with('toto-dict-FR FR', ['label1' => 'fr1']); + + Dict::SetDefaultLanguage('RU RU'); + Dict::SetUserLanguage('FR FR'); + $this->assertEquals('fr1', Dict::S('label1')); + } + + public function testInitLangIfNeeded_Apc_PropertyInDefaultLanguageDictionnary(){ + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(true); + + $this->oApcService->expects($this->exactly(2)) + ->method('apc_fetch') + ->withConsecutive(['toto-dict-FR FR'], ['toto-dict-RU RU']) + ->willReturnOnConsecutiveCalls([], false); + + $this->oApcService->expects($this->exactly(1)) + ->method('apc_store') + ->withConsecutive(['toto-dict-RU RU', ['label1' => 'ru1', 'label2' => 'ru2']] + ); + + Dict::SetDefaultLanguage('RU RU'); + Dict::SetUserLanguage('FR FR'); + $this->assertEquals('ru2', Dict::S('label2')); + } + + public function testInitLangIfNeeded_ApcCorrupted_PropertyInDefaultLanguageDictionnary(){ + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(true); + + $this->oApcService->expects($this->exactly(2)) + ->method('apc_fetch') + ->withConsecutive(['toto-dict-FR FR'], ['toto-dict-RU RU']) + ->willReturnOnConsecutiveCalls([], 'corrupteddata'); + + $this->oApcService->expects($this->exactly(1)) + ->method('apc_store') + ->withConsecutive(['toto-dict-RU RU', ['label1' => 'ru1', 'label2' => 'ru2']] + ); + + Dict::SetDefaultLanguage('RU RU'); + Dict::SetUserLanguage('FR FR'); + $this->assertEquals('ru2', Dict::S('label2')); + } + + public function testInitLangIfNeeded_Apc_PropertyInDictDefaultLanguageDictionnary(){ + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(true); + + $this->oApcService->expects($this->exactly(3)) + ->method('apc_fetch') + ->withConsecutive(['toto-dict-FR FR'], ['toto-dict-RU RU'], ['toto-dict-EN US']) + ->willReturnOnConsecutiveCalls([], [], false); + + $this->oApcService->expects($this->exactly(1)) + ->method('apc_store') + ->withConsecutive( + ['toto-dict-EN US', ['label1' => 'en1', 'label2' => 'en2', 'label3' => 'en3']] + ); + + Dict::SetDefaultLanguage('RU RU'); + Dict::SetUserLanguage('FR FR'); + $this->assertEquals('en3', Dict::S('label3')); + } + + public function testInitLangIfNeeded_ApcCorrupted_PropertyInDictDefaultLanguageDictionnary(){ + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(true); + + $this->oApcService->expects($this->exactly(3)) + ->method('apc_fetch') + ->withConsecutive(['toto-dict-FR FR'], ['toto-dict-RU RU'], ['toto-dict-EN US']) + ->willReturnOnConsecutiveCalls([], [], 'corrupted'); + + $this->oApcService->expects($this->exactly(1)) + ->method('apc_store') + ->withConsecutive( + ['toto-dict-EN US', ['label1' => 'en1', 'label2' => 'en2', 'label3' => 'en3']] + ); + + Dict::SetDefaultLanguage('RU RU'); + Dict::SetUserLanguage('FR FR'); + $this->assertEquals('en3', Dict::S('label3')); + } + + public function testInitLangIfNeeded_Apc_PropertyNotFound(){ + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(true); + + $this->oApcService->expects($this->exactly(3)) + ->method('apc_fetch') + ->withConsecutive(['toto-dict-FR FR'], ['toto-dict-RU RU'], ['toto-dict-EN US']) + ->willReturnOnConsecutiveCalls([], [], []); + + $this->oApcService->expects($this->exactly(0)) + ->method('apc_store'); + + Dict::SetDefaultLanguage('RU RU'); + Dict::SetUserLanguage('FR FR'); + $this->assertEquals('undefined_label', Dict::S('undefined_label')); + } +} diff --git a/test/core/dictTest.php b/test/core/dictTest.php index a65764848..bd2179947 100644 --- a/test/core/dictTest.php +++ b/test/core/dictTest.php @@ -38,12 +38,49 @@ use Exception; */ class dictTest extends ItopTestCase { + private $sEnvName; protected function setUp() { parent::setUp(); require_once (APPROOT.'core/coreexception.class.inc.php'); require_once (APPROOT.'core/dict.class.inc.php'); - require_once 'mockDict.incphp'; + require_once (APPROOT.'core/apc-service.class.inc.php'); + $this->sEnvName = date("c"); + $sDictionaryFolder = APPROOT."env-$this->sEnvName/dictionaries"; + @mkdir($sDictionaryFolder, 0777, true); + + $sContent = << 'gabu', +)); +PHP; + file_put_contents("$sDictionaryFolder/fr-fr.dict.php", $sContent); + $sContent = << 'zomeu', +)); +PHP; + file_put_contents("$sDictionaryFolder/en-en.dict.php", $sContent); + + $_SESSION['itop_env'] = $this->sEnvName; + //require_once 'mockDict.incphp'; + } + + protected function tearDown() + { + foreach (glob(APPROOT."env-$this->sEnvName/dictionaries/*") as $sFile){ + unlink($sFile); + } + rmdir(APPROOT."env-$this->sEnvName/dictionaries"); + rmdir(APPROOT."env-$this->sEnvName"); } /** @@ -51,7 +88,32 @@ class dictTest extends ItopTestCase */ public function testType() { + $_SESSION['itop_env'] = 'production'; $this->assertInternalType('string', Dict::S('Core:AttributeURL')); $this->assertInternalType('string', Dict::Format('Change:AttName_SetTo', '1', '2')); } + + public function testInitLangIfNeeded_NoApc(){ + $oApcService = $this->createMock(\ApcService::class); + Dict::SetApcService($oApcService); + Dict::EnableCache('toto'); + + $oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(false); + + $oApcService->expects($this->never()) + ->method('apc_fetch') + ->willReturn(false); + + $oApcService->expects($this->never()) + ->method('apc_store') + ->willReturn(false); + + Dict::SetLanguagesList(['FR FR' => 'fr', 'EN EN' => 'en']); + Dict::SetUserLanguage('FR FR'); + $this->assertEquals('gabu', Dict::S('label1')); + Dict::SetUserLanguage('EN EN'); + $this->assertEquals('zomeu', Dict::S('label1')); + } } From 2d705c66978e3ab7ff56ad8778bce2139c320085 Mon Sep 17 00:00:00 2001 From: odain Date: Thu, 1 Jul 2021 17:38:59 +0200 Subject: [PATCH 02/16] add autoload --- lib/composer/autoload_classmap.php | 1 + lib/composer/autoload_static.php | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index d3c442c0d..321dbd99d 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -17,6 +17,7 @@ return array( 'ActionChecker' => $baseDir . '/core/userrights.class.inc.php', 'ActionEmail' => $baseDir . '/core/action.class.inc.php', 'ActionNotification' => $baseDir . '/core/action.class.inc.php', + 'ApcService' => $baseDir . '/core/apc-service.class.inc.php', 'ApplicationContext' => $baseDir . '/application/applicationcontext.class.inc.php', 'ApplicationException' => $baseDir . '/application/application.inc.php', 'ApplicationMenu' => $baseDir . '/application/menunode.class.inc.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 502ed9369..67e125821 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -247,6 +247,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'ActionChecker' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', 'ActionEmail' => __DIR__ . '/../..' . '/core/action.class.inc.php', 'ActionNotification' => __DIR__ . '/../..' . '/core/action.class.inc.php', + 'ApcService' => __DIR__ . '/../..' . '/core/apc-service.class.inc.php', 'ApplicationContext' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php', 'ApplicationException' => __DIR__ . '/../..' . '/application/application.inc.php', 'ApplicationMenu' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', From ed719e13c71adbac9df3aaf09fdfe8a1b3b49cba Mon Sep 17 00:00:00 2001 From: odain Date: Fri, 2 Jul 2021 08:53:58 +0200 Subject: [PATCH 03/16] =?UTF-8?q?N=C2=B04125=20-=20Add=20a=20warning=20log?= =?UTF-8?q?=20when=20corrupted=20data=20returned=20by=20APCu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/dict.class.inc.php | 1 + 1 file changed, 1 insertion(+) diff --git a/core/dict.class.inc.php b/core/dict.class.inc.php index df644ce84..909213096 100644 --- a/core/dict.class.inc.php +++ b/core/dict.class.inc.php @@ -273,6 +273,7 @@ class Dict self::$m_aData[$sLangCode] = self::GetApcService()->apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode); if (! is_array(self::$m_aData[$sLangCode])) { + IssueLog::Warning("APCu corrupted data (with $sLangCode dictionnary). APCu configuration and running version should be troubleshooted..."); unset(self::$m_aData[$sLangCode]); } else From 2fe42652235d29e6cf88871f79ddca6e230e1d3f Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 28 Jul 2021 11:14:49 +0200 Subject: [PATCH 04/16] =?UTF-8?q?N=C2=B04125=20-=20Make=20translations=20l?= =?UTF-8?q?oading=20more=20robust=20toward=20APCu=20cache=20corruption=20o?= =?UTF-8?q?r=20invalid=20dictionnary=20-=20adaptations=20to=20get=20curren?= =?UTF-8?q?t=20correction=20accepted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/dict.class.inc.php | 12 +++++------ test/core/dictApcuTest.php | 42 ++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/core/dict.class.inc.php b/core/dict.class.inc.php index 909213096..c309639e5 100644 --- a/core/dict.class.inc.php +++ b/core/dict.class.inc.php @@ -271,13 +271,13 @@ class Dict // Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter // self::$m_aData[$sLangCode] = self::GetApcService()->apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode); - if (! is_array(self::$m_aData[$sLangCode])) - { - IssueLog::Warning("APCu corrupted data (with $sLangCode dictionnary). APCu configuration and running version should be troubleshooted..."); + if (self::$m_aData[$sLangCode] === false) { unset(self::$m_aData[$sLangCode]); - } - else - { + } else if (! is_array(self::$m_aData[$sLangCode])) { + IssueLog::Warning("APCu corrupted data (with $sLangCode dictionnary). APCu configuration and running version should be troubleshooted..."); + //N°4125: we dont fix issue on iTop side. just add some log to be aware of it and fix APCu instead. + $bResult = true; + } else { $bResult = true; } } diff --git a/test/core/dictApcuTest.php b/test/core/dictApcuTest.php index 96ace5b7f..1aada7ca7 100644 --- a/test/core/dictApcuTest.php +++ b/test/core/dictApcuTest.php @@ -269,23 +269,24 @@ STR; $this->assertEquals('fr1', Dict::S('label1')); } + //corrupted data not fixed + //we will return label from another dictionary (defaut one => russian here) public function testInitLangIfNeeded_Apc_CorruptedCache_PropertyInUserDictionnary(){ $this->oApcService->expects($this->any()) ->method('function_exists') ->willReturn(true); - $this->oApcService->expects($this->exactly(1)) + $this->oApcService->expects($this->exactly(2)) ->method('apc_fetch') - ->with('toto-dict-FR FR') - ->willReturn('label1'); + ->withConsecutive(['toto-dict-FR FR'], ['toto-dict-RU RU']) + ->willReturnOnConsecutiveCalls('corrupteddata', ['label1' => 'ru1']); - $this->oApcService->expects($this->exactly(1)) - ->method('apc_store') - ->with('toto-dict-FR FR', ['label1' => 'fr1']); + $this->oApcService->expects($this->exactly(0)) + ->method('apc_store'); Dict::SetDefaultLanguage('RU RU'); Dict::SetUserLanguage('FR FR'); - $this->assertEquals('fr1', Dict::S('label1')); + $this->assertEquals('ru1', Dict::S('label1')); } public function testInitLangIfNeeded_Apc_PropertyInDefaultLanguageDictionnary(){ @@ -308,24 +309,24 @@ STR; $this->assertEquals('ru2', Dict::S('label2')); } + //corrupted data not fixed + //we will return label from default language dictionary (EN here) public function testInitLangIfNeeded_ApcCorrupted_PropertyInDefaultLanguageDictionnary(){ $this->oApcService->expects($this->any()) ->method('function_exists') ->willReturn(true); - $this->oApcService->expects($this->exactly(2)) + $this->oApcService->expects($this->exactly(3)) ->method('apc_fetch') - ->withConsecutive(['toto-dict-FR FR'], ['toto-dict-RU RU']) - ->willReturnOnConsecutiveCalls([], 'corrupteddata'); + ->withConsecutive(['toto-dict-FR FR'], ['toto-dict-RU RU'], ['toto-dict-EN US']) + ->willReturnOnConsecutiveCalls([], 'corrupteddata', ['label1' => 'en1', 'label2' => 'en2', 'label3' => 'en3']); - $this->oApcService->expects($this->exactly(1)) - ->method('apc_store') - ->withConsecutive(['toto-dict-RU RU', ['label1' => 'ru1', 'label2' => 'ru2']] - ); + $this->oApcService->expects($this->exactly(0)) + ->method('apc_store'); Dict::SetDefaultLanguage('RU RU'); Dict::SetUserLanguage('FR FR'); - $this->assertEquals('ru2', Dict::S('label2')); + $this->assertEquals('en2', Dict::S('label2')); } public function testInitLangIfNeeded_Apc_PropertyInDictDefaultLanguageDictionnary(){ @@ -357,17 +358,14 @@ STR; $this->oApcService->expects($this->exactly(3)) ->method('apc_fetch') ->withConsecutive(['toto-dict-FR FR'], ['toto-dict-RU RU'], ['toto-dict-EN US']) - ->willReturnOnConsecutiveCalls([], [], 'corrupted'); + ->willReturnOnConsecutiveCalls([], [], 'corrupteddata'); - $this->oApcService->expects($this->exactly(1)) - ->method('apc_store') - ->withConsecutive( - ['toto-dict-EN US', ['label1' => 'en1', 'label2' => 'en2', 'label3' => 'en3']] - ); + $this->oApcService->expects($this->exactly(0)) + ->method('apc_store'); Dict::SetDefaultLanguage('RU RU'); Dict::SetUserLanguage('FR FR'); - $this->assertEquals('en3', Dict::S('label3')); + $this->assertEquals('label3', Dict::S('label3')); } public function testInitLangIfNeeded_Apc_PropertyNotFound(){ From a7e54d4bad51a44828be6d80d9a74fef9d93e216 Mon Sep 17 00:00:00 2001 From: odain Date: Tue, 9 Nov 2021 17:08:08 +0100 Subject: [PATCH 05/16] =?UTF-8?q?N=C2=B04125=20-=20fix=20infinite=20loop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/apc-service.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/apc-service.class.inc.php b/core/apc-service.class.inc.php index 6d367e8fa..07a787b6a 100644 --- a/core/apc-service.class.inc.php +++ b/core/apc-service.class.inc.php @@ -5,7 +5,7 @@ class ApcService { } public function function_exists($function_name) { - return $this->function_exists($function_name); + return function_exists($function_name); } /** From 865f9f4f673f5e241b34f792bbcc553fe8adeb02 Mon Sep 17 00:00:00 2001 From: odain Date: Tue, 9 Nov 2021 17:14:13 +0100 Subject: [PATCH 06/16] add @since annotation + @see from Hispka --- core/apc-service.class.inc.php | 6 ++++++ core/dict.class.inc.php | 2 ++ 2 files changed, 8 insertions(+) diff --git a/core/apc-service.class.inc.php b/core/apc-service.class.inc.php index 07a787b6a..760bc980e 100644 --- a/core/apc-service.class.inc.php +++ b/core/apc-service.class.inc.php @@ -1,5 +1,9 @@ Date: Fri, 26 Nov 2021 09:12:58 +0100 Subject: [PATCH 07/16] =?UTF-8?q?=20N=C2=B04125=20-=20test=20did=20not=20r?= =?UTF-8?q?un=20under=20windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/core/dictApcuTest.php | 16 ++++++++-------- test/core/dictTest.php | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/core/dictApcuTest.php b/test/core/dictApcuTest.php index 1aada7ca7..0c48427d2 100644 --- a/test/core/dictApcuTest.php +++ b/test/core/dictApcuTest.php @@ -45,9 +45,9 @@ class dictApcuTest extends ItopTestCase protected function setUp() { parent::setUp(); - require_once (APPROOT.'core/coreexception.class.inc.php'); - require_once (APPROOT.'core/dict.class.inc.php'); - require_once (APPROOT.'core/apc-service.class.inc.php'); + require_once (APPROOT.'core' . DIRECTORY_SEPARATOR . 'coreexception.class.inc.php'); + require_once (APPROOT.'core' . DIRECTORY_SEPARATOR . 'dict.class.inc.php'); + require_once (APPROOT.'core' . DIRECTORY_SEPARATOR . 'apc-service.class.inc.php'); $this->sEnvName = date("c"); $_SESSION['itop_env'] = $this->sEnvName; @@ -63,7 +63,7 @@ class dictApcuTest extends ItopTestCase private function InitDictionnaries(){ clearstatcache(); - $this->sDictionaryFolder = APPROOT."env-$this->sEnvName/dictionaries"; + $this->sDictionaryFolder = APPROOT."env-$this->sEnvName" . DIRECTORY_SEPARATOR . "dictionaries"; @mkdir($this->sDictionaryFolder, 0777, true); $sLabels = <<sEnvName/dictionaries/*") as $sFile){ + foreach (glob(APPROOT."env-$this->sEnvName" . DIRECTORY_SEPARATOR . "dictionaries" . DIRECTORY_SEPARATOR . "*") as $sFile){ unlink($sFile); } - rmdir(APPROOT."env-$this->sEnvName/dictionaries"); + rmdir(APPROOT."env-$this->sEnvName" . DIRECTORY_SEPARATOR . "dictionaries"); rmdir(APPROOT."env-$this->sEnvName"); } diff --git a/test/core/dictTest.php b/test/core/dictTest.php index bd2179947..f57b48d5b 100644 --- a/test/core/dictTest.php +++ b/test/core/dictTest.php @@ -42,11 +42,11 @@ class dictTest extends ItopTestCase protected function setUp() { parent::setUp(); - require_once (APPROOT.'core/coreexception.class.inc.php'); - require_once (APPROOT.'core/dict.class.inc.php'); - require_once (APPROOT.'core/apc-service.class.inc.php'); + require_once (APPROOT.'core'. DIRECTORY_SEPARATOR . 'coreexception.class.inc.php'); + require_once (APPROOT.'core'. DIRECTORY_SEPARATOR . 'dict.class.inc.php'); + require_once (APPROOT.'core'. DIRECTORY_SEPARATOR . 'apc-service.class.inc.php'); $this->sEnvName = date("c"); - $sDictionaryFolder = APPROOT."env-$this->sEnvName/dictionaries"; + $sDictionaryFolder = APPROOT."env-$this->sEnvName" . DIRECTORY_SEPARATOR . "dictionaries"; @mkdir($sDictionaryFolder, 0777, true); $sContent = << 'gabu', )); PHP; - file_put_contents("$sDictionaryFolder/fr-fr.dict.php", $sContent); + file_put_contents($sDictionaryFolder . DIRECTORY_SEPARATOR . "fr-fr.dict.php", $sContent); $sContent = << 'zomeu', )); PHP; - file_put_contents("$sDictionaryFolder/en-en.dict.php", $sContent); + file_put_contents($sDictionaryFolder . DIRECTORY_SEPARATOR . "en-en.dict.php", $sContent); $_SESSION['itop_env'] = $this->sEnvName; //require_once 'mockDict.incphp'; @@ -76,10 +76,10 @@ PHP; protected function tearDown() { - foreach (glob(APPROOT."env-$this->sEnvName/dictionaries/*") as $sFile){ + foreach (glob(APPROOT."env-$this->sEnvName" . DIRECTORY_SEPARATOR . "dictionaries" . DIRECTORY_SEPARATOR . "*") as $sFile){ unlink($sFile); } - rmdir(APPROOT."env-$this->sEnvName/dictionaries"); + rmdir(APPROOT."env-$this->sEnvName" . DIRECTORY_SEPARATOR . "dictionaries"); rmdir(APPROOT."env-$this->sEnvName"); } From f1037147a9f55363ef4e209d894002f45d3d568d Mon Sep 17 00:00:00 2001 From: odain Date: Fri, 26 Nov 2021 09:17:19 +0100 Subject: [PATCH 08/16] =?UTF-8?q?N=C2=B04125=20-=20phpdoc=20only=20coming?= =?UTF-8?q?=20from=20pull=20request=20Thanks=20to=20Hispka=20and=20=20piRG?= =?UTF-8?q?oif?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/apc-service.class.inc.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/apc-service.class.inc.php b/core/apc-service.class.inc.php index 760bc980e..0d462f19f 100644 --- a/core/apc-service.class.inc.php +++ b/core/apc-service.class.inc.php @@ -8,14 +8,19 @@ class ApcService { public function __construct() { } + /** + * @param string $function_name + * @return bool + * @see function_exists() + */ public function function_exists($function_name) { return function_exists($function_name); } /** - * @param $key string|array + * @param string|array $key * @return mixed - * @see apc_fetch + * @see apc_fetch() */ function apc_fetch($key) { @@ -27,7 +32,7 @@ class ApcService { * @param $var * @param int $ttl * @return array|bool - * @see apc_store + * @see apc_store() */ function apc_store($key, $var = NULL, $ttl = 0) { From 0cbf34ba5a7587439407dd2156cec220d5057b46 Mon Sep 17 00:00:00 2001 From: odain Date: Fri, 26 Nov 2021 09:47:01 +0100 Subject: [PATCH 09/16] =?UTF-8?q?N=C2=B04125=20-=20fix=20tests=20under=20w?= =?UTF-8?q?indows=20(again)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/core/dictApcuTest.php | 2 +- test/core/dictTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/dictApcuTest.php b/test/core/dictApcuTest.php index 0c48427d2..a1b3c21d9 100644 --- a/test/core/dictApcuTest.php +++ b/test/core/dictApcuTest.php @@ -49,7 +49,7 @@ class dictApcuTest extends ItopTestCase require_once (APPROOT.'core' . DIRECTORY_SEPARATOR . 'dict.class.inc.php'); require_once (APPROOT.'core' . DIRECTORY_SEPARATOR . 'apc-service.class.inc.php'); - $this->sEnvName = date("c"); + $this->sEnvName = time(); $_SESSION['itop_env'] = $this->sEnvName; $this->oApcService = $this->createMock(\ApcService::class); diff --git a/test/core/dictTest.php b/test/core/dictTest.php index f57b48d5b..5bea10386 100644 --- a/test/core/dictTest.php +++ b/test/core/dictTest.php @@ -45,7 +45,7 @@ class dictTest extends ItopTestCase require_once (APPROOT.'core'. DIRECTORY_SEPARATOR . 'coreexception.class.inc.php'); require_once (APPROOT.'core'. DIRECTORY_SEPARATOR . 'dict.class.inc.php'); require_once (APPROOT.'core'. DIRECTORY_SEPARATOR . 'apc-service.class.inc.php'); - $this->sEnvName = date("c"); + $this->sEnvName = time(); $sDictionaryFolder = APPROOT."env-$this->sEnvName" . DIRECTORY_SEPARATOR . "dictionaries"; @mkdir($sDictionaryFolder, 0777, true); From dcfdb2d0a9d5e2be363db091131718981bb7db2a Mon Sep 17 00:00:00 2001 From: odain Date: Fri, 26 Nov 2021 10:28:56 +0100 Subject: [PATCH 10/16] =?UTF-8?q?N=C2=B04125=20-=20add=20integration=20tes?= =?UTF-8?q?t=20apart=20from=20ApcService=20mocking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/core/dictApcuTest.php | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/test/core/dictApcuTest.php b/test/core/dictApcuTest.php index a1b3c21d9..cce8060f9 100644 --- a/test/core/dictApcuTest.php +++ b/test/core/dictApcuTest.php @@ -119,16 +119,30 @@ PHP; rmdir(APPROOT."env-$this->sEnvName"); } - public function testInitLangIfNeeded_NoApc(){ - $this->oApcService->expects($this->any()) - ->method('function_exists') - ->willReturn(false); + public function InitLangIfNeeded_NoApcProvider(){ + return [ + 'apcu mocked' => [ 'bApcuMocked' => true ], + 'integration test - apcu service like in production - install php-apcu before' => [ 'bApcuMocked' => false ], + ]; + } - $this->oApcService->expects($this->never()) - ->method('apc_fetch'); + /** + * @dataProvider InitLangIfNeeded_NoApcProvider + */ + public function testInitLangIfNeeded_NoApc($bApcuMocked){ + if ($bApcuMocked) { + $this->oApcService->expects($this->any()) + ->method('function_exists') + ->willReturn(false); - $this->oApcService->expects($this->never()) - ->method('apc_store'); + $this->oApcService->expects($this->never()) + ->method('apc_fetch'); + + $this->oApcService->expects($this->never()) + ->method('apc_store'); + } else { + Dict::SetApcService(null); + } //EN US default language $this->assertEquals('en1', Dict::S('label1')); From 3511867ba34e0162dbb4890e24de8afe4f3aba26 Mon Sep 17 00:00:00 2001 From: odain Date: Fri, 26 Nov 2021 17:07:20 +0100 Subject: [PATCH 11/16] =?UTF-8?q?N=C2=B04125=20-=20use=20Error=20log=20lev?= =?UTF-8?q?el=20+=20apc=20dedicated=20log=20channel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/dict.class.inc.php | 2 +- core/log.class.inc.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/dict.class.inc.php b/core/dict.class.inc.php index c64c3a717..18b88f2ba 100644 --- a/core/dict.class.inc.php +++ b/core/dict.class.inc.php @@ -152,7 +152,7 @@ class Dict if (!array_key_exists($sLangCode, self::$m_aData)) { - IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed"); + IssueLog::Error("Cannot find $sLangCode in dictionnaries. default labels displayed", LogChannels::APC); // It may happen, when something happens before the dictionaries get loaded return $sStringCode; } diff --git a/core/log.class.inc.php b/core/log.class.inc.php index 39988557c..739db5082 100644 --- a/core/log.class.inc.php +++ b/core/log.class.inc.php @@ -542,6 +542,7 @@ class LogChannels const DEADLOCK = 'DeadLock'; const INLINE_IMAGE = 'InlineImage'; const PORTAL = 'portal'; + const APC = 'apc'; } From e4c68936a0ad9fd80f77c13f6711402d48e037dc Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 29 Nov 2021 09:23:05 +0100 Subject: [PATCH 12/16] =?UTF-8?q?N=C2=B04125=20-=20log=20error=20in=20an?= =?UTF-8?q?=20apc=20channel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/dict.class.inc.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/dict.class.inc.php b/core/dict.class.inc.php index 18b88f2ba..629094a52 100644 --- a/core/dict.class.inc.php +++ b/core/dict.class.inc.php @@ -152,7 +152,7 @@ class Dict if (!array_key_exists($sLangCode, self::$m_aData)) { - IssueLog::Error("Cannot find $sLangCode in dictionnaries. default labels displayed", LogChannels::APC); + IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed"); // It may happen, when something happens before the dictionaries get loaded return $sStringCode; } @@ -276,8 +276,9 @@ class Dict if (self::$m_aData[$sLangCode] === false) { unset(self::$m_aData[$sLangCode]); } else if (! is_array(self::$m_aData[$sLangCode])) { - IssueLog::Warning("APCu corrupted data (with $sLangCode dictionnary). APCu configuration and running version should be troubleshooted..."); - //N°4125: we dont fix issue on iTop side. just add some log to be aware of it and fix APCu instead. + // N°4125: we dont fix dictionnary corrupted cache (on iTop side). + // but we log an error in a dedicated channel to let itop administrator be aware of a potential APCu issue to fix. + IssueLog::Error("APCu corrupted data (with $sLangCode dictionnary). APCu configuration and running version should be troubleshooted...", LogChannels::APC); $bResult = true; } else { $bResult = true; From 5ed71ecb9614a3f0ef9031501f21d8463dfc4f6c Mon Sep 17 00:00:00 2001 From: odain Date: Thu, 2 Dec 2021 12:21:54 +0100 Subject: [PATCH 13/16] =?UTF-8?q?N=C2=B04125=20-=20fix=20file=20ending=20(?= =?UTF-8?q?hipska=20remark)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/apc-service.class.inc.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/apc-service.class.inc.php b/core/apc-service.class.inc.php index 0d462f19f..ca541eb84 100644 --- a/core/apc-service.class.inc.php +++ b/core/apc-service.class.inc.php @@ -38,4 +38,5 @@ class ApcService { { return apc_store($key, $var, $ttl); } -} \ No newline at end of file +} +?> \ No newline at end of file From 910bbe11607321c96e4405d82a2ab17299e9d9f6 Mon Sep 17 00:00:00 2001 From: Stephen Abello Date: Fri, 3 Dec 2021 11:10:52 +0100 Subject: [PATCH 14/16] =?UTF-8?q?N=C2=B04501=20Security=20hardening?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- datamodels/2.x/itop-attachments/renderers.itop-attachments.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datamodels/2.x/itop-attachments/renderers.itop-attachments.php b/datamodels/2.x/itop-attachments/renderers.itop-attachments.php index 16c848fbd..531afecc8 100644 --- a/datamodels/2.x/itop-attachments/renderers.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/renderers.itop-attachments.php @@ -551,7 +551,7 @@ JS $sFileName$sAttachmentMeta $sFileFormattedSize $sAttachmentDateFormatted - $sAttachmentUploader + $sAttachmentUploaderForHtml $sFileType $sDeleteColumn From 5e61388b654e5c461fc91e6bffbc110a632da9bb Mon Sep 17 00:00:00 2001 From: Stephen Abello Date: Tue, 7 Dec 2021 09:56:20 +0100 Subject: [PATCH 15/16] =?UTF-8?q?N=C2=B04495=20Security=20hardening?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/search/search_form_criteria_enum.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/search/search_form_criteria_enum.js b/js/search/search_form_criteria_enum.js index 9a8ecadd4..12d682584 100644 --- a/js/search/search_form_criteria_enum.js +++ b/js/search/search_form_criteria_enum.js @@ -225,7 +225,7 @@ $(function() for (var i in aSortedValues) { var sValCode = aSortedValues[i][0]; - var sValLabel = aSortedValues[i][1]; + var sValLabel = $('
').html(aSortedValues[i][1]).text(); var oValueElem = this._makeListItemElement(sValLabel, sValCode); oValueElem.appendTo(oDynamicListElem); From f916f9cde893a6d1491a4f414bd39aa7331b7d51 Mon Sep 17 00:00:00 2001 From: Pierre Goiffon Date: Wed, 8 Dec 2021 16:47:42 +0100 Subject: [PATCH 16/16] =?UTF-8?q?N=C2=B04289=20Allow=20to=20use=20privUITr?= =?UTF-8?q?ansactionFile=20when=20no=20user=20logged=20Before=20we=20were?= =?UTF-8?q?=20throwing=20a=20SecurityException,=20which=20was=20blocking?= =?UTF-8?q?=20for=20combodo-unauthenticated-form=20for=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/transaction.class.inc.php | 11 +++++++---- test/application/privUITransactionFileTest.php | 8 ++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/application/transaction.class.inc.php b/application/transaction.class.inc.php index 053c71891..6310c8c33 100644 --- a/application/transaction.class.inc.php +++ b/application/transaction.class.inc.php @@ -193,16 +193,19 @@ class privUITransactionSession */ class privUITransactionFile { + /** @var int Value to use when no user logged */ + const UNAUTHENTICATED_USER_ID = -666; + /** - * @return int - * @throws \SecurityException if no connected user + * @return int current user id, or {@see self::UNAUTHENTICATED_USER_ID} if no user logged * * @since 2.6.5 2.7.6 3.0.0 N°4289 method creation */ - private static function GetCurrentUserId() { + private static function GetCurrentUserId() + { $iCurrentUserId = UserRights::GetConnectedUserId(); if ('' === $iCurrentUserId) { - throw new SecurityException('Cannot creation transaction_id when no user logged'); + $iCurrentUserId = static::UNAUTHENTICATED_USER_ID; } return $iCurrentUserId; diff --git a/test/application/privUITransactionFileTest.php b/test/application/privUITransactionFileTest.php index d416e3e22..e8e734625 100644 --- a/test/application/privUITransactionFileTest.php +++ b/test/application/privUITransactionFileTest.php @@ -37,5 +37,13 @@ class privUITransactionFileTest extends \Combodo\iTop\Test\UnitTest\ItopDataTest $this->assertTrue($bUser1Login2, 'Login with user1 throw an error'); $bResult = privUITransactionFile::RemoveTransaction($sTransactionIdUserSupport); $this->assertTrue($bResult, 'Token created by support user must be removed in the support user context'); + + // test when no user logged (combodo-unauthenticated-form module for example) + UserRights::_ResetSessionCache(); + $sTransactionIdUnauthenticatedUser = privUITransactionFile::GetNewTransactionId(); + $bResult = privUITransactionFile::IsTransactionValid($sTransactionIdUnauthenticatedUser, false); + $this->assertTrue($bResult, 'Token created by unauthenticated user must be valid when no user logged'); + $bResult = privUITransactionFile::RemoveTransaction($sTransactionIdUnauthenticatedUser); + $this->assertTrue($bResult, 'Token created by unauthenticated user must be removed when no user logged'); } } \ No newline at end of file