restservices.class.inc.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. <?php
  2. // Copyright (C) 2013-2015 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. /**
  19. * REST/json services
  20. *
  21. * Definition of common structures + the very minimum service provider (manage objects)
  22. *
  23. * @package REST Services
  24. * @copyright Copyright (C) 2013 Combodo SARL
  25. * @license http://opensource.org/licenses/AGPL-3.0
  26. * @api
  27. */
  28. /**
  29. * Element of the response formed by RestResultWithObjects
  30. *
  31. * @package REST Services
  32. */
  33. class ObjectResult
  34. {
  35. public $code;
  36. public $message;
  37. public $class;
  38. public $key;
  39. public $fields;
  40. /**
  41. * Default constructor
  42. */
  43. public function __construct($sClass = null, $iId = null)
  44. {
  45. $this->code = RestResult::OK;
  46. $this->message = '';
  47. $this->class = $sClass;
  48. $this->key = $iId;
  49. $this->fields = array();
  50. }
  51. /**
  52. * Helper to make an output value for a given attribute
  53. *
  54. * @param DBObject $oObject The object being reported
  55. * @param string $sAttCode The attribute code (must be valid)
  56. * @param boolean $bExtendedOutput Output all of the link set attributes ?
  57. * @return string A scalar representation of the value
  58. */
  59. protected function MakeResultValue(DBObject $oObject, $sAttCode, $bExtendedOutput = false)
  60. {
  61. if ($sAttCode == 'id')
  62. {
  63. $value = $oObject->GetKey();
  64. }
  65. else
  66. {
  67. $sClass = get_class($oObject);
  68. $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
  69. if ($oAttDef instanceof AttributeLinkedSet)
  70. {
  71. // Iterate on the set and build an array of array of attcode=>value
  72. $oSet = $oObject->Get($sAttCode);
  73. $value = array();
  74. while ($oLnk = $oSet->Fetch())
  75. {
  76. $sLnkRefClass = $bExtendedOutput ? get_class($oLnk) : $oAttDef->GetLinkedClass();
  77. $aLnkValues = array();
  78. foreach (MetaModel::ListAttributeDefs($sLnkRefClass) as $sLnkAttCode => $oLnkAttDef)
  79. {
  80. // Skip attributes pointing to the current object (redundant data)
  81. if ($sLnkAttCode == $oAttDef->GetExtKeyToMe())
  82. {
  83. continue;
  84. }
  85. // Skip any attribute of the link that points to the current object
  86. $oLnkAttDef = MetaModel::GetAttributeDef($sLnkRefClass, $sLnkAttCode);
  87. if (method_exists($oLnkAttDef, 'GetKeyAttCode'))
  88. {
  89. if ($oLnkAttDef->GetKeyAttCode() == $oAttDef->GetExtKeyToMe())
  90. {
  91. continue;
  92. }
  93. }
  94. $aLnkValues[$sLnkAttCode] = $this->MakeResultValue($oLnk, $sLnkAttCode, $bExtendedOutput);
  95. }
  96. $value[] = $aLnkValues;
  97. }
  98. }
  99. else
  100. {
  101. $value = $oAttDef->GetForJSON($oObject->Get($sAttCode));
  102. }
  103. }
  104. return $value;
  105. }
  106. /**
  107. * Report the value for the given object attribute
  108. *
  109. * @param DBObject $oObject The object being reported
  110. * @param string $sAttCode The attribute code (must be valid)
  111. * @param boolean $bExtendedOutput Output all of the link set attributes ?
  112. * @return void
  113. */
  114. public function AddField(DBObject $oObject, $sAttCode, $bExtendedOutput = false)
  115. {
  116. $this->fields[$sAttCode] = $this->MakeResultValue($oObject, $sAttCode, $bExtendedOutput);
  117. }
  118. }
  119. /**
  120. * REST response for services managing objects. Derive this structure to add information and/or constants
  121. *
  122. * @package Extensibility
  123. * @package REST Services
  124. * @api
  125. */
  126. class RestResultWithObjects extends RestResult
  127. {
  128. public $objects;
  129. /**
  130. * Report the given object
  131. *
  132. * @param int An error code (RestResult::OK is no issue has been found)
  133. * @param string $sMessage Description of the error if any, an empty string otherwise
  134. * @param DBObject $oObject The object being reported
  135. * @param array $aFieldSpec An array of class => attribute codes (Cf. RestUtils::GetFieldList). List of the attributes to be reported.
  136. * @param boolean $bExtendedOutput Output all of the link set attributes ?
  137. * @return void
  138. */
  139. public function AddObject($iCode, $sMessage, $oObject, $aFieldSpec = null, $bExtendedOutput = false)
  140. {
  141. $sClass = get_class($oObject);
  142. $oObjRes = new ObjectResult($sClass, $oObject->GetKey());
  143. $oObjRes->code = $iCode;
  144. $oObjRes->message = $sMessage;
  145. $aFields = null;
  146. if (!is_null($aFieldSpec))
  147. {
  148. // Enum all classes in the hierarchy, starting with the current one
  149. foreach (MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, false) as $sRefClass)
  150. {
  151. if (array_key_exists($sRefClass, $aFieldSpec))
  152. {
  153. $aFields = $aFieldSpec[$sRefClass];
  154. break;
  155. }
  156. }
  157. }
  158. if (is_null($aFields))
  159. {
  160. // No fieldspec given, or not found...
  161. $aFields = array('id', 'friendlyname');
  162. }
  163. foreach ($aFields as $sAttCode)
  164. {
  165. $oObjRes->AddField($oObject, $sAttCode, $bExtendedOutput);
  166. }
  167. $sObjKey = get_class($oObject).'::'.$oObject->GetKey();
  168. $this->objects[$sObjKey] = $oObjRes;
  169. }
  170. }
  171. class RestResultWithRelations extends RestResultWithObjects
  172. {
  173. public $relations;
  174. public function __construct()
  175. {
  176. parent::__construct();
  177. $this->relations = array();
  178. }
  179. public function AddRelation($sSrcKey, $sDestKey)
  180. {
  181. if (!array_key_exists($sSrcKey, $this->relations))
  182. {
  183. $this->relations[$sSrcKey] = array();
  184. }
  185. $this->relations[$sSrcKey][] = array('key' => $sDestKey);
  186. }
  187. }
  188. /**
  189. * Deletion result codes for a target object (either deleted or updated)
  190. *
  191. * @package Extensibility
  192. * @api
  193. * @since 2.0.1
  194. */
  195. class RestDelete
  196. {
  197. /**
  198. * Result: Object deleted as per the initial request
  199. */
  200. const OK = 0;
  201. /**
  202. * Result: general issue (user rights or ... ?)
  203. */
  204. const ISSUE = 1;
  205. /**
  206. * Result: Must be deleted to preserve database integrity
  207. */
  208. const AUTO_DELETE = 2;
  209. /**
  210. * Result: Must be deleted to preserve database integrity, but that is NOT possible
  211. */
  212. const AUTO_DELETE_ISSUE = 3;
  213. /**
  214. * Result: Must be deleted to preserve database integrity, but this must be requested explicitely
  215. */
  216. const REQUEST_EXPLICITELY = 4;
  217. /**
  218. * Result: Must be updated to preserve database integrity
  219. */
  220. const AUTO_UPDATE = 5;
  221. /**
  222. * Result: Must be updated to preserve database integrity, but that is NOT possible
  223. */
  224. const AUTO_UPDATE_ISSUE = 6;
  225. }
  226. /**
  227. * Implementation of core REST services (create/get/update... objects)
  228. *
  229. * @package Core
  230. */
  231. class CoreServices implements iRestServiceProvider
  232. {
  233. /**
  234. * Enumerate services delivered by this class
  235. *
  236. * @param string $sVersion The version (e.g. 1.0) supported by the services
  237. * @return array An array of hash 'verb' => verb, 'description' => description
  238. */
  239. public function ListOperations($sVersion)
  240. {
  241. // 1.3 - iTop 2.2.0, Verb 'get_related': added the options 'redundancy' and 'direction' to take into account the redundancy in the impact analysis
  242. // 1.2 - was documented in the wiki but never released ! Same as 1.3
  243. // 1.1 - In the reply, objects have a 'key' entry so that it is no more necessary to split class::key programmaticaly
  244. // 1.0 - Initial implementation in iTop 2.0.1
  245. //
  246. $aOps = array();
  247. if (in_array($sVersion, array('1.0', '1.1', '1.2', '1.3')))
  248. {
  249. $aOps[] = array(
  250. 'verb' => 'core/create',
  251. 'description' => 'Create an object'
  252. );
  253. $aOps[] = array(
  254. 'verb' => 'core/update',
  255. 'description' => 'Update an object'
  256. );
  257. $aOps[] = array(
  258. 'verb' => 'core/apply_stimulus',
  259. 'description' => 'Apply a stimulus to change the state of an object'
  260. );
  261. $aOps[] = array(
  262. 'verb' => 'core/get',
  263. 'description' => 'Search for objects'
  264. );
  265. $aOps[] = array(
  266. 'verb' => 'core/delete',
  267. 'description' => 'Delete objects'
  268. );
  269. $aOps[] = array(
  270. 'verb' => 'core/get_related',
  271. 'description' => 'Get related objects through the specified relation'
  272. );
  273. $aOps[] = array(
  274. 'verb' => 'core/check_credentials',
  275. 'description' => 'Check user credentials'
  276. );
  277. }
  278. return $aOps;
  279. }
  280. /**
  281. * Enumerate services delivered by this class
  282. * @param string $sVersion The version (e.g. 1.0) supported by the services
  283. * @return RestResult The standardized result structure (at least a message)
  284. * @throws Exception in case of internal failure.
  285. */
  286. public function ExecOperation($sVersion, $sVerb, $aParams)
  287. {
  288. $oResult = new RestResultWithObjects();
  289. switch ($sVerb)
  290. {
  291. case 'core/create':
  292. RestUtils::InitTrackingComment($aParams);
  293. $sClass = RestUtils::GetClass($aParams, 'class');
  294. $aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
  295. $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
  296. $bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
  297. if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) != UR_ALLOWED_YES)
  298. {
  299. $oResult->code = RestResult::UNAUTHORIZED;
  300. $oResult->message = "The current user does not have enough permissions for creating data of class $sClass";
  301. }
  302. elseif (UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY) != UR_ALLOWED_YES)
  303. {
  304. $oResult->code = RestResult::UNAUTHORIZED;
  305. $oResult->message = "The current user does not have enough permissions for massively creating data of class $sClass";
  306. }
  307. else
  308. {
  309. $oObject = RestUtils::MakeObjectFromFields($sClass, $aFields);
  310. $oObject->DBInsert();
  311. $oResult->AddObject(0, 'created', $oObject, $aShowFields, $bExtendedOutput);
  312. }
  313. break;
  314. case 'core/update':
  315. RestUtils::InitTrackingComment($aParams);
  316. $sClass = RestUtils::GetClass($aParams, 'class');
  317. $key = RestUtils::GetMandatoryParam($aParams, 'key');
  318. $aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
  319. $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
  320. $bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
  321. // Note: the target class cannot be based on the result of FindObjectFromKey, because in case the user does not have read access, that function already fails with msg 'Nothing found'
  322. $sTargetClass = RestUtils::GetObjectSetFromKey($sClass, $key)->GetFilter()->GetClass();
  323. if (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_MODIFY) != UR_ALLOWED_YES)
  324. {
  325. $oResult->code = RestResult::UNAUTHORIZED;
  326. $oResult->message = "The current user does not have enough permissions for modifying data of class $sTargetClass";
  327. }
  328. elseif (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_MODIFY) != UR_ALLOWED_YES)
  329. {
  330. $oResult->code = RestResult::UNAUTHORIZED;
  331. $oResult->message = "The current user does not have enough permissions for massively modifying data of class $sTargetClass";
  332. }
  333. else
  334. {
  335. $oObject = RestUtils::FindObjectFromKey($sClass, $key);
  336. RestUtils::UpdateObjectFromFields($oObject, $aFields);
  337. $oObject->DBUpdate();
  338. $oResult->AddObject(0, 'updated', $oObject, $aShowFields, $bExtendedOutput);
  339. }
  340. break;
  341. case 'core/apply_stimulus':
  342. RestUtils::InitTrackingComment($aParams);
  343. $sClass = RestUtils::GetClass($aParams, 'class');
  344. $key = RestUtils::GetMandatoryParam($aParams, 'key');
  345. $aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
  346. $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
  347. $bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
  348. $sStimulus = RestUtils::GetMandatoryParam($aParams, 'stimulus');
  349. // Note: the target class cannot be based on the result of FindObjectFromKey, because in case the user does not have read access, that function already fails with msg 'Nothing found'
  350. $sTargetClass = RestUtils::GetObjectSetFromKey($sClass, $key)->GetFilter()->GetClass();
  351. if (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_MODIFY) != UR_ALLOWED_YES)
  352. {
  353. $oResult->code = RestResult::UNAUTHORIZED;
  354. $oResult->message = "The current user does not have enough permissions for modifying data of class $sTargetClass";
  355. }
  356. elseif (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_BULK_MODIFY) != UR_ALLOWED_YES)
  357. {
  358. $oResult->code = RestResult::UNAUTHORIZED;
  359. $oResult->message = "The current user does not have enough permissions for massively modifying data of class $sTargetClass";
  360. }
  361. else
  362. {
  363. $oObject = RestUtils::FindObjectFromKey($sClass, $key);
  364. RestUtils::UpdateObjectFromFields($oObject, $aFields);
  365. $aTransitions = $oObject->EnumTransitions();
  366. $aStimuli = MetaModel::EnumStimuli(get_class($oObject));
  367. if (!isset($aTransitions[$sStimulus]))
  368. {
  369. // Invalid stimulus
  370. $oResult->code = RestResult::INTERNAL_ERROR;
  371. $oResult->message = "Invalid stimulus: '$sStimulus' on the object ".$oObject->GetName()." in state '".$oObject->GetState()."'";
  372. }
  373. else
  374. {
  375. $aTransition = $aTransitions[$sStimulus];
  376. $sTargetState = $aTransition['target_state'];
  377. $aStates = MetaModel::EnumStates($sClass);
  378. $aTargetStateDef = $aStates[$sTargetState];
  379. $aExpectedAttributes = $aTargetStateDef['attribute_list'];
  380. $aMissingMandatory = array();
  381. foreach($aExpectedAttributes as $sAttCode => $iExpectCode)
  382. {
  383. if ( ($iExpectCode & OPT_ATT_MANDATORY) && ($oObject->Get($sAttCode) == ''))
  384. {
  385. $aMissingMandatory[] = $sAttCode;
  386. }
  387. }
  388. if (count($aMissingMandatory) == 0)
  389. {
  390. // If all the mandatory fields are already present, just apply the transition silently...
  391. if ($oObject->ApplyStimulus($sStimulus))
  392. {
  393. $oObject->DBUpdate();
  394. $oResult->AddObject(0, 'updated', $oObject, $aShowFields, $bExtendedOutput);
  395. }
  396. }
  397. else
  398. {
  399. // Missing mandatory attributes for the transition
  400. $oResult->code = RestResult::INTERNAL_ERROR;
  401. $oResult->message = 'Missing mandatory attribute(s) for applying the stimulus: '.implode(', ', $aMissingMandatory).'.';
  402. }
  403. }
  404. }
  405. break;
  406. case 'core/get':
  407. $sClass = RestUtils::GetClass($aParams, 'class');
  408. $key = RestUtils::GetMandatoryParam($aParams, 'key');
  409. $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
  410. $bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
  411. $oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
  412. $sTargetClass = $oObjectSet->GetFilter()->GetClass();
  413. if (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ) != UR_ALLOWED_YES)
  414. {
  415. $oResult->code = RestResult::UNAUTHORIZED;
  416. $oResult->message = "The current user does not have enough permissions for reading data of class $sTargetClass";
  417. }
  418. elseif (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_BULK_READ) != UR_ALLOWED_YES)
  419. {
  420. $oResult->code = RestResult::UNAUTHORIZED;
  421. $oResult->message = "The current user does not have enough permissions for exporting data of class $sTargetClass";
  422. }
  423. else
  424. {
  425. while ($oObject = $oObjectSet->Fetch())
  426. {
  427. $oResult->AddObject(0, '', $oObject, $aShowFields, $bExtendedOutput);
  428. }
  429. $oResult->message = "Found: ".$oObjectSet->Count();
  430. }
  431. break;
  432. case 'core/delete':
  433. $sClass = RestUtils::GetClass($aParams, 'class');
  434. $key = RestUtils::GetMandatoryParam($aParams, 'key');
  435. $bSimulate = RestUtils::GetOptionalParam($aParams, 'simulate', false);
  436. $oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
  437. $sTargetClass = $oObjectSet->GetFilter()->GetClass();
  438. if (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_DELETE) != UR_ALLOWED_YES)
  439. {
  440. $oResult->code = RestResult::UNAUTHORIZED;
  441. $oResult->message = "The current user does not have enough permissions for deleting data of class $sTargetClass";
  442. }
  443. elseif (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_DELETE) != UR_ALLOWED_YES)
  444. {
  445. $oResult->code = RestResult::UNAUTHORIZED;
  446. $oResult->message = "The current user does not have enough permissions for massively deleting data of class $sTargetClass";
  447. }
  448. else
  449. {
  450. $aObjects = $oObjectSet->ToArray();
  451. $this->DeleteObjects($oResult, $aObjects, $bSimulate);
  452. }
  453. break;
  454. case 'core/get_related':
  455. $oResult = new RestResultWithRelations();
  456. $sClass = RestUtils::GetClass($aParams, 'class');
  457. $key = RestUtils::GetMandatoryParam($aParams, 'key');
  458. $sRelation = RestUtils::GetMandatoryParam($aParams, 'relation');
  459. $iMaxRecursionDepth = RestUtils::GetOptionalParam($aParams, 'depth', 20 /* = MAX_RECURSION_DEPTH */);
  460. $sDirection = RestUtils::GetOptionalParam($aParams, 'direction', null);
  461. $bEnableRedundancy = RestUtils::GetOptionalParam($aParams, 'redundancy', false);
  462. $bReverse = false;
  463. if (is_null($sDirection) && ($sRelation == 'depends on'))
  464. {
  465. // Legacy behavior, consider "depends on" as a forward relation
  466. $sRelation = 'impacts';
  467. $sDirection = 'up';
  468. $bReverse = true; // emulate the legacy behavior by returning the edges
  469. }
  470. else if(is_null($sDirection))
  471. {
  472. $sDirection = 'down';
  473. }
  474. $oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
  475. if ($sDirection == 'down')
  476. {
  477. $oRelationGraph = $oObjectSet->GetRelatedObjectsDown($sRelation, $iMaxRecursionDepth, $bEnableRedundancy);
  478. }
  479. else if ($sDirection == 'up')
  480. {
  481. $oRelationGraph = $oObjectSet->GetRelatedObjectsUp($sRelation, $iMaxRecursionDepth, $bEnableRedundancy);
  482. }
  483. else
  484. {
  485. $oResult->code = RestResult::INTERNAL_ERROR;
  486. $oResult->message = "Invalid value: '$sDirection' for the parameter 'direction'. Valid values are 'up' and 'down'";
  487. return $oResult;
  488. }
  489. if ($bEnableRedundancy)
  490. {
  491. // Remove the redundancy nodes from the output
  492. $oIterator = new RelationTypeIterator($oRelationGraph, 'Node');
  493. foreach($oIterator as $oNode)
  494. {
  495. if ($oNode instanceof RelationRedundancyNode)
  496. {
  497. $oRelationGraph->FilterNode($oNode);
  498. }
  499. }
  500. }
  501. $aIndexByClass = array();
  502. $oIterator = new RelationTypeIterator($oRelationGraph);
  503. foreach($oIterator as $oElement)
  504. {
  505. if ($oElement instanceof RelationObjectNode)
  506. {
  507. $oObject = $oElement->GetProperty('object');
  508. if ($oObject)
  509. {
  510. if ($bEnableRedundancy)
  511. {
  512. // Add only the "reached" objects
  513. if ($oElement->GetProperty('is_reached'))
  514. {
  515. $aIndexByClass[get_class($oObject)][$oObject->GetKey()] = null;
  516. $oResult->AddObject(0, '', $oObject);
  517. }
  518. }
  519. else
  520. {
  521. $aIndexByClass[get_class($oObject)][$oObject->GetKey()] = null;
  522. $oResult->AddObject(0, '', $oObject);
  523. }
  524. }
  525. }
  526. else if ($oElement instanceof RelationEdge)
  527. {
  528. $oSrcObj = $oElement->GetSourceNode()->GetProperty('object');
  529. $oDestObj = $oElement->GetSinkNode()->GetProperty('object');
  530. $sSrcKey = get_class($oSrcObj).'::'.$oSrcObj->GetKey();
  531. $sDestKey = get_class($oDestObj).'::'.$oDestObj->GetKey();
  532. if ($bEnableRedundancy)
  533. {
  534. // Add only the edges where both source and destination are "reached"
  535. if ($oElement->GetSourceNode()->GetProperty('is_reached') && $oElement->GetSinkNode()->GetProperty('is_reached'))
  536. {
  537. if ($bReverse)
  538. {
  539. $oResult->AddRelation($sDestKey, $sSrcKey);
  540. }
  541. else
  542. {
  543. $oResult->AddRelation($sSrcKey, $sDestKey);
  544. }
  545. }
  546. }
  547. else
  548. {
  549. if ($bReverse)
  550. {
  551. $oResult->AddRelation($sDestKey, $sSrcKey);
  552. }
  553. else
  554. {
  555. $oResult->AddRelation($sSrcKey, $sDestKey);
  556. }
  557. }
  558. }
  559. }
  560. if (count($aIndexByClass) > 0)
  561. {
  562. $aStats = array();
  563. $aUnauthorizedClasses = array();
  564. foreach ($aIndexByClass as $sClass => $aIds)
  565. {
  566. if (UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_READ) != UR_ALLOWED_YES)
  567. {
  568. $aUnauthorizedClasses[$sClass] = true;
  569. }
  570. $aStats[] = $sClass.'= '.count($aIds);
  571. }
  572. if (count($aUnauthorizedClasses) > 0)
  573. {
  574. $sClasses = implode(', ', array_keys($aUnauthorizedClasses));
  575. $oResult = new RestResult();
  576. $oResult->code = RestResult::UNAUTHORIZED;
  577. $oResult->message = "The current user does not have enough permissions for exporting data of class(es): $sClasses";
  578. }
  579. else
  580. {
  581. $oResult->message = "Scope: ".$oObjectSet->Count()."; Related objects: ".implode(', ', $aStats);
  582. }
  583. }
  584. else
  585. {
  586. $oResult->message = "Nothing found";
  587. }
  588. break;
  589. case 'core/check_credentials':
  590. $oResult = new RestResult();
  591. $sUser = RestUtils::GetMandatoryParam($aParams, 'user');
  592. $sPassword = RestUtils::GetMandatoryParam($aParams, 'password');
  593. if (UserRights::CheckCredentials($sUser, $sPassword) !== true)
  594. {
  595. $oResult->authorized = false;
  596. }
  597. else
  598. {
  599. $oResult->authorized = true;
  600. }
  601. break;
  602. default:
  603. // unknown operation: handled at a higher level
  604. }
  605. return $oResult;
  606. }
  607. /**
  608. * Helper for object deletion
  609. */
  610. public function DeleteObjects($oResult, $aObjects, $bSimulate)
  611. {
  612. $oDeletionPlan = new DeletionPlan();
  613. foreach($aObjects as $oObj)
  614. {
  615. if ($bSimulate)
  616. {
  617. $oObj->CheckToDelete($oDeletionPlan);
  618. }
  619. else
  620. {
  621. $oObj->DBDelete($oDeletionPlan);
  622. }
  623. }
  624. foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes)
  625. {
  626. foreach ($aDeletes as $iId => $aData)
  627. {
  628. $oToDelete = $aData['to_delete'];
  629. $bAutoDel = (($aData['mode'] == DEL_SILENT) || ($aData['mode'] == DEL_AUTO));
  630. if (array_key_exists('issue', $aData))
  631. {
  632. if ($bAutoDel)
  633. {
  634. if (isset($aData['requested_explicitely'])) // i.e. in the initial list of objects to delete
  635. {
  636. $iCode = RestDelete::ISSUE;
  637. $sPlanned = 'Cannot be deleted: '.$aData['issue'];
  638. }
  639. else
  640. {
  641. $iCode = RestDelete::AUTO_DELETE_ISSUE;
  642. $sPlanned = 'Should be deleted automatically... but: '.$aData['issue'];
  643. }
  644. }
  645. else
  646. {
  647. $iCode = RestDelete::REQUEST_EXPLICITELY;
  648. $sPlanned = 'Must be deleted explicitely... but: '.$aData['issue'];
  649. }
  650. }
  651. else
  652. {
  653. if ($bAutoDel)
  654. {
  655. if (isset($aData['requested_explicitely']))
  656. {
  657. $iCode = RestDelete::OK;
  658. $sPlanned = '';
  659. }
  660. else
  661. {
  662. $iCode = RestDelete::AUTO_DELETE;
  663. $sPlanned = 'Deleted automatically';
  664. }
  665. }
  666. else
  667. {
  668. $iCode = RestDelete::REQUEST_EXPLICITELY;
  669. $sPlanned = 'Must be deleted explicitely';
  670. }
  671. }
  672. $oResult->AddObject($iCode, $sPlanned, $oToDelete);
  673. }
  674. }
  675. foreach ($oDeletionPlan->ListUpdates() as $sRemoteClass => $aToUpdate)
  676. {
  677. foreach ($aToUpdate as $iId => $aData)
  678. {
  679. $oToUpdate = $aData['to_reset'];
  680. if (array_key_exists('issue', $aData))
  681. {
  682. $iCode = RestDelete::AUTO_UPDATE_ISSUE;
  683. $sPlanned = 'Should be updated automatically... but: '.$aData['issue'];
  684. }
  685. else
  686. {
  687. $iCode = RestDelete::AUTO_UPDATE;
  688. $sPlanned = 'Reset external keys: '.$aData['attributes_list'];
  689. }
  690. $oResult->AddObject($iCode, $sPlanned, $oToUpdate);
  691. }
  692. }
  693. if ($oDeletionPlan->FoundStopper())
  694. {
  695. if ($oDeletionPlan->FoundSecurityIssue())
  696. {
  697. $iRes = RestResult::UNAUTHORIZED;
  698. $sRes = 'Deletion not allowed on some objects';
  699. }
  700. elseif ($oDeletionPlan->FoundManualOperation())
  701. {
  702. $iRes = RestResult::UNSAFE;
  703. $sRes = 'The deletion requires that other objects be deleted/updated, and those operations must be requested explicitely';
  704. }
  705. else
  706. {
  707. $iRes = RestResult::INTERNAL_ERROR;
  708. $sRes = 'Some issues have been encountered. See the list of planned changes for more information about the issue(s).';
  709. }
  710. }
  711. else
  712. {
  713. $iRes = RestResult::OK;
  714. $sRes = 'Deleted: '.count($aObjects);
  715. $iIndirect = $oDeletionPlan->GetTargetCount() - count($aObjects);
  716. if ($iIndirect > 0)
  717. {
  718. $sRes .= ' plus (for DB integrity) '.$iIndirect;
  719. }
  720. }
  721. $oResult->code = $iRes;
  722. if ($bSimulate)
  723. {
  724. $oResult->message = 'SIMULATING: '.$sRes;
  725. }
  726. else
  727. {
  728. $oResult->message = $sRes;
  729. }
  730. }
  731. }