From e4a3a7d0ca52214c6bd7bec612255ef7669ab93c Mon Sep 17 00:00:00 2001 From: Anne-Catherine <57360138+accognet@users.noreply.github.com> Date: Wed, 17 Apr 2024 14:38:33 +0200 Subject: [PATCH 1/3] =?UTF-8?q?N=C2=B06962=20-=20Impact=20analysis=20does?= =?UTF-8?q?=20not=20fully=20take=20into=20account=20the=20user=20rights=20?= =?UTF-8?q?with=20sharing=20base=20(#572)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/config.class.inc.php | 8 ++ core/relationgraph.class.inc.php | 107 +++++++++++++----- .../unitary-tests/core/RelationGraphTest.php | 102 +++++++++++++++++ 3 files changed, 190 insertions(+), 27 deletions(-) create mode 100644 tests/php-unit-tests/unitary-tests/core/RelationGraphTest.php diff --git a/core/config.class.inc.php b/core/config.class.inc.php index a2f7300d5..1491c6ab1 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -1209,6 +1209,14 @@ class Config 'source_of_value' => '', 'show_in_conf_sample' => false, ], + 'relations.complete_analysis' => [ + 'type' => 'bool', + 'description' => 'Continue the impact/depends analysis even if a step is not visible to the user', + 'default' => false, + 'value' => '', + 'source_of_value' => '', + 'show_in_conf_sample' => false, + ], 'sessions_tracking.enabled' => [ 'type' => 'bool', 'description' => 'Whether or not the whole mechanism to track active sessions is enabled. See PHP session.gc_maxlifetime setting to configure session expiration.', diff --git a/core/relationgraph.class.inc.php b/core/relationgraph.class.inc.php index 30c542cbb..e723f5547 100644 --- a/core/relationgraph.class.inc.php +++ b/core/relationgraph.class.inc.php @@ -20,7 +20,7 @@ /** * Data structures (i.e. PHP classes) to build and use relation graphs * - * @copyright Copyright (C) 2015-2023 Combodo SARL + * @copyright Copyright (C) 2015-2024 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 * */ @@ -367,6 +367,9 @@ class RelationGraph extends SimpleGraph $oNode->ReachDown('is_reached', true); } } + if ( MetaModel::GetConfig()->Get('relations.complete_analysis')) { + $this->ApplyUserRightsOnGraph(); + } } /** @@ -401,6 +404,9 @@ class RelationGraph extends SimpleGraph $oNode->ReachDown('is_reached', true); } } + if ( MetaModel::GetConfig()->Get('relations.complete_analysis')) { + $this->ApplyUserRightsOnGraph(); + } } @@ -460,6 +466,10 @@ class RelationGraph extends SimpleGraph try { $oFlt = static::MakeSearch($sQuery); + if ( MetaModel::GetConfig()->Get('relations.complete_analysis')) { + //no filter to find all impacts + $oFlt->AllowAllData(true); + } $oObjSet = new DBObjectSet($oFlt, array(), $oObject->ToArgsForQuery()); $oRelatedObj = $oObjSet->Fetch(); } @@ -474,27 +484,24 @@ class RelationGraph extends SimpleGraph { set_time_limit(intval($iLoopTimeLimit)); - $sObjectRef = RelationObjectNode::MakeId($oRelatedObj); - $oRelatedNode = $this->GetNode($sObjectRef); - if (is_null($oRelatedNode)) - { - $oRelatedNode = new RelationObjectNode($this, $oRelatedObj); - } - $oSourceNode = $bDown ? $oObjectNode : $oRelatedNode; - $oSinkNode = $bDown ? $oRelatedNode : $oObjectNode; + $sObjectRef = RelationObjectNode::MakeId($oRelatedObj); + $oRelatedNode = $this->GetNode($sObjectRef); + if (is_null($oRelatedNode)) { + $oRelatedNode = new RelationObjectNode($this, $oRelatedObj); + } + $oSourceNode = $bDown ? $oObjectNode : $oRelatedNode; + $oSinkNode = $bDown ? $oRelatedNode : $oObjectNode; if ($bEnableRedundancy) { - $oRedundancyNode = $this->ComputeRedundancy($sRelCode, $aQueryInfo, $oSourceNode, $oSinkNode); - } - else - { - $oRedundancyNode = null; - } - if (!$oRedundancyNode) - { - // Direct link (otherwise handled by ComputeRedundancy) - new RelationEdge($this, $oSourceNode, $oSinkNode); - } + $oRedundancyNode = $this->ComputeRedundancy($sRelCode, $aQueryInfo, $oSourceNode, $oSinkNode); + } else { + $oRedundancyNode = null; + } + if (!$oRedundancyNode) { + // Direct link (otherwise handled by ComputeRedundancy) + new RelationEdge($this, $oSourceNode, $oSinkNode); + } + // Recurse $this->AddRelatedObjects($sRelCode, $bDown, $oRelatedNode, $iMaxDepth - 1, $bEnableRedundancy); } @@ -538,6 +545,10 @@ class RelationGraph extends SimpleGraph try { $oFlt = static::MakeSearch($sQuery); + if ( MetaModel::GetConfig()->Get('relations.complete_analysis')) { + //no filter to find all impacts + $oFlt->AllowAllData(true); + } $oObjSet = new DBObjectSet($oFlt, array(), $oObject->ToArgsForQuery()); $iCount = $oObjSet->Count(); } @@ -553,13 +564,12 @@ class RelationGraph extends SimpleGraph while ($oUpperObj = $oObjSet->Fetch()) { - $sObjectRef = RelationObjectNode::MakeId($oUpperObj); - $oUpperNode = $this->GetNode($sObjectRef); - if (is_null($oUpperNode)) - { - $oUpperNode = new RelationObjectNode($this, $oUpperObj); - } - new RelationEdge($this, $oUpperNode, $oRedundancyNode); + $sObjectRef = RelationObjectNode::MakeId($oUpperObj); + $oUpperNode = $this->GetNode($sObjectRef); + if (is_null($oUpperNode)) { + $oUpperNode = new RelationObjectNode($this, $oUpperObj); + } + new RelationEdge($this, $oUpperNode, $oRedundancyNode); } } } @@ -694,4 +704,47 @@ class RelationGraph extends SimpleGraph $oSearch->SetArchiveMode(false); return $oSearch; } + + + /** + * @return void + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \MySQLException + * @throws \OQLException + * @throws \SimpleGraphException + */ + private function ApplyUserRightsOnGraph() + { + //The chart is complete. Now we need to control which objects are allowed to the current user. + if (!UserRights::IsAdministrator()) { + //First we get all the objects presents in chart in $aArrayTest + $oIterator = new RelationTypeIterator($this, 'Node'); + $aArrayTest = []; + foreach ($oIterator as $oNode) { + $oObj = $oNode->GetProperty('object'); + if ($oObj) { + $aArrayTest[get_class($oObj)][$oObj->GetKey()] = $oObj->GetKey(); + } + } + //Then for each class, we made a request to control access rights + // visible objects are removed from $aArrayTest + foreach ($aArrayTest as $sClass => $aKeys) { + $sOQL = "SELECT ".$sClass.' WHERE id IN ('.implode(',', $aKeys).')'; + $oSearch = DBObjectSearch::FromOQL($sOQL); + $aListId = $oSearch->SelectAttributeToArray('id'); + foreach($aListId as$aItem ) { + unset($aArrayTest[$sClass][$aItem['id']]); + } + } + //then removes from the graph all objects still present in $aArrayTest + foreach ($oIterator as $oNode) { + $oObj = $oNode->GetProperty('object'); + if ($oObj && isset($aArrayTest[get_class($oObj)]) && in_array($oObj->GetKey(), $aArrayTest[get_class($oObj)])) { + $this->FilterNode($oNode); + } + } + } + } + } diff --git a/tests/php-unit-tests/unitary-tests/core/RelationGraphTest.php b/tests/php-unit-tests/unitary-tests/core/RelationGraphTest.php new file mode 100644 index 000000000..153a8b8f4 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/core/RelationGraphTest.php @@ -0,0 +1,102 @@ + array('Server',1), + 'Server::2' => array('Server',2), + ); + } + + /** + * @dataProvider ComputeRelatedObjectsProvider + * + * @param $sClass + * @param $iKey + * + * @return void + * @throws \ArchivedObjectException + * @throws \CoreException + */ + public function testComputeRelatedObjectsDown($sClass, $iKey) + { + $oServer = MetaModel::GetObject($sClass, $iKey); + MetaModel::GetConfig()->Set('relations.complete_analysis', true); + + $oGraphTrue = new RelationGraph(); + $oGraphTrue->AddSourceObject($oServer); + $oGraphTrue->ComputeRelatedObjectsDown('impacts', 10, true); + + + MetaModel::GetConfig()->Set('relations.complete_analysis', false); + $oGraphFalse = new RelationGraph(); + $oGraphFalse->AddSourceObject($oServer); + $oGraphFalse->ComputeRelatedObjectsDown('impacts', 10, true); + + $aNodeFalse = $oGraphFalse->_GetNodes(); + $aNodeTrue = $oGraphFalse->_GetNodes(); + + //test if the 2 graph contains the same objects + $this->assertEquals(count($aNodeFalse), count($aNodeFalse),'With the admin user, the impact analysis down must have the same number of impacted items whatever the value of the "relations.complete_analysis" parameter.'); + foreach ($aNodeTrue as $sKey =>$oNodeTrue){ + $this->assertArrayHasKey($sKey, $aNodeFalse,'With the admin user, the impact analysis down must have the same results whatever the value of the "relations.complete_analysis" parameter.'); + } + } + + /** + * @dataProvider ComputeRelatedObjectsProvider + * + * @param $sClass + * @param $iKey + * + * @return void + * @throws \ArchivedObjectException + * @throws \CoreException + */ + public function testComputeRelatedObjectsUp($sClass, $iKey) + { + $oServer = MetaModel::GetObject($sClass, $iKey); + MetaModel::GetConfig()->Set('relations.complete_analysis', true); + + $oGraphTrue = new RelationGraph(); + $oGraphTrue->AddSourceObject($oServer); + $oGraphTrue->ComputeRelatedObjectsUp('impacts', 10, true); + + + MetaModel::GetConfig()->Set('relations.complete_analysis', false); + $oGraphFalse = new RelationGraph(); + $oGraphFalse->AddSourceObject($oServer); + $oGraphFalse->ComputeRelatedObjectsUp('impacts', 10, true); + + $aNodeFalse = $oGraphFalse->_GetNodes(); + $aNodeTrue = $oGraphFalse->_GetNodes(); + + //test if the 2 graph contains the same objects + $this->assertEquals(count($aNodeFalse), count($aNodeFalse),'With the admin user, the impact analysis up must have the same number of impacted items whatever the value of the "relations.complete_analysis" parameter.'); + foreach ($aNodeTrue as $sKey =>$oNodeTrue){ + $this->assertArrayHasKey($sKey, $aNodeFalse,'With the admin user, the impact analysis up must have the same results whatever the value of the "relations.complete_analysis" parameter.'); + } + } + +} From ddc004a14794941d5d1456e5ebbc9b3d7423877c Mon Sep 17 00:00:00 2001 From: Anne-Cath Date: Wed, 17 Apr 2024 15:44:52 +0200 Subject: [PATCH 2/3] =?UTF-8?q?N=C2=B07461=20-=20Portal:=20import=20user?= =?UTF-8?q?=20photo=20don't=20like=20some=20pictures=20with=20PHP>=3D8.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/utils.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/utils.inc.php b/application/utils.inc.php index b1e11e818..5368e61c9 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -2226,8 +2226,8 @@ SQL; $fScale = min($iMaxImageWidth / $iWidth, $iMaxImageHeight / $iHeight); - $iNewWidth = $iWidth * $fScale; - $iNewHeight = $iHeight * $fScale; + $iNewWidth = floor($iWidth * $fScale); + $iNewHeight = floor($iHeight * $fScale); $new = imagecreatetruecolor($iNewWidth, $iNewHeight); From c56aeb08f51c04d3ac1318eb42f12b7f46d3ac3f Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass <95754414+bdalsass@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:04:07 +0200 Subject: [PATCH 3/3] =?UTF-8?q?N=C2=B02443=20-=20Boolean=20don't=20accept?= =?UTF-8?q?=20yes/no=20value=20(#645)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AttributeBoolean form field edition is now handled by list or radio --- application/cmdbabstract.class.inc.php | 7 +++++++ core/attributedef.class.inc.php | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index ee8cb7ac4..b7b37e0ce 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -2609,6 +2609,13 @@ JS $iFieldSize = $oAttDef->GetMaxSize(); if ($aAllowedValues !== null) { + + // convert AttributeBoolean value due to issue with radio style when value is false + // @see N°2443 - Boolean don't accept yes/no value + if($oAttDef instanceof AttributeBoolean){ + $value = $value === false ? 0 : 1; + } + // Discrete list of values, use a SELECT or RADIO buttons depending on the config $sDisplayStyle = $oAttDef->GetDisplayStyle(); switch ($sDisplayStyle) diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 799623557..672907969 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -3572,6 +3572,19 @@ class AttributeBoolean extends AttributeInteger { return CMDBChangeOpSetAttributeScalar::class; } + + public function GetAllowedValues($aArgs = array(), $sContains = '') : array + { + return [ + 0 => $this->GetValueLabel(false), + 1 => $this->GetValueLabel(true) + ]; + } + + public function GetDisplayStyle() + { + return $this->GetOptional('display_style', 'select'); + } } /**