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.'); + } + } + +}