rest.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. <?php
  2. // Copyright (C) 2010-2013 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. * Entry point for all the REST services
  20. *
  21. * --------------------------------------------------
  22. * Create an object
  23. * --------------------------------------------------
  24. * POST itop/webservices/rest.php
  25. * {
  26. * operation: 'object_create',
  27. * comment: 'Synchronization from blah...',
  28. * class: 'UserRequest',
  29. * results: 'id, friendlyname',
  30. * fields:
  31. * {
  32. * org_id: 'SELECT Organization WHERE name = "Demo"',
  33. * caller_id:
  34. * {
  35. * name: 'monet',
  36. * first_name: 'claude',
  37. * }
  38. * title: 'Houston, got a problem!',
  39. * description: 'The fridge is empty'
  40. * contacts_list:
  41. * [
  42. * {
  43. * role: 'pizza delivery',
  44. * contact_id:
  45. * {
  46. * finalclass: 'Person',
  47. * name: 'monet',
  48. * first_name: 'claude'
  49. * }
  50. * }
  51. * ]
  52. * }
  53. * }
  54. *
  55. *
  56. * @copyright Copyright (C) 2010-2013 Combodo SARL
  57. * @license http://opensource.org/licenses/AGPL-3.0
  58. */
  59. if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
  60. require_once(__DIR__.'/../approot.inc.php');
  61. require_once(APPROOT.'/application/application.inc.php');
  62. require_once(APPROOT.'/application/loginwebpage.class.inc.php');
  63. require_once(APPROOT.'/application/ajaxwebpage.class.inc.php');
  64. require_once(APPROOT.'/application/startup.inc.php');
  65. require_once(APPROOT.'core/restservices.class.inc.php');
  66. /**
  67. * Result structure that is specific to the hardcoded verb 'list_operations'
  68. */
  69. class RestResultListOperations extends RestResult
  70. {
  71. public $version;
  72. public $operations;
  73. public function AddOperation($sVerb, $sDescription, $sServiceProviderClass)
  74. {
  75. $this->operations[] = array(
  76. 'verb' => $sVerb,
  77. 'description' => $sDescription,
  78. 'extension' => $sServiceProviderClass
  79. );
  80. }
  81. }
  82. if (!function_exists('json_last_error_msg')) {
  83. function json_last_error_msg() {
  84. static $ERRORS = array(
  85. JSON_ERROR_NONE => 'No error',
  86. JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
  87. JSON_ERROR_STATE_MISMATCH => 'State mismatch (invalid or malformed JSON)',
  88. JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
  89. JSON_ERROR_SYNTAX => 'Syntax error',
  90. JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded'
  91. );
  92. $error = json_last_error();
  93. return isset($ERRORS[$error]) ? $ERRORS[$error] : 'Unknown error';
  94. }
  95. }
  96. ////////////////////////////////////////////////////////////////////////////////
  97. //
  98. // Main
  99. //
  100. $oP = new ajax_page('rest');
  101. $oCtx = new ContextTag('REST/JSON');
  102. $sVersion = utils::ReadParam('version', null, false, 'raw_data');
  103. $sOperation = utils::ReadParam('operation', null);
  104. $sJsonString = utils::ReadParam('json_data', null, false, 'raw_data');
  105. $sProvider = '';
  106. try
  107. {
  108. utils::UseParamFile();
  109. $iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN); // Starting with iTop 2.2.0 portal users are no longer allowed to access the REST/JSON API
  110. if ($iRet != LoginWebPage::EXIT_CODE_OK)
  111. {
  112. switch($iRet)
  113. {
  114. case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
  115. throw new Exception("Missing parameter 'auth_user'", RestResult::MISSING_AUTH_USER);
  116. break;
  117. case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
  118. throw new Exception("Missing parameter 'auth_pwd'", RestResult::MISSING_AUTH_PWD);
  119. break;
  120. case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
  121. throw new Exception("Invalid login", RestResult::UNAUTHORIZED);
  122. break;
  123. case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
  124. throw new Exception("Portal user is not allowed", RestResult::UNAUTHORIZED);
  125. break;
  126. default:
  127. throw new Exception("Unknown authentication error (retCode=$iRet)", RestResult::UNAUTHORIZED);
  128. }
  129. }
  130. if ($sVersion == null)
  131. {
  132. throw new Exception("Missing parameter 'version' (e.g. '1.0')", RestResult::MISSING_VERSION);
  133. }
  134. if ($sJsonString == null)
  135. {
  136. throw new Exception("Missing parameter 'json_data", RestResult::MISSING_JSON);
  137. }
  138. $aJsonData = @json_decode($sJsonString);
  139. if ($aJsonData == null)
  140. {
  141. throw new Exception("Parameter json_data is not a valid JSON structure", RestResult::INVALID_JSON);
  142. }
  143. $aProviders = array();
  144. foreach(get_declared_classes() as $sPHPClass)
  145. {
  146. $oRefClass = new ReflectionClass($sPHPClass);
  147. if ($oRefClass->implementsInterface('iRestServiceProvider'))
  148. {
  149. $aProviders[] = new $sPHPClass;
  150. }
  151. }
  152. $aOpToRestService = array(); // verb => $oRestServiceProvider
  153. foreach ($aProviders as $oRestSP)
  154. {
  155. $aOperations = $oRestSP->ListOperations($sVersion);
  156. foreach ($aOperations as $aOpData)
  157. {
  158. $aOpToRestService[$aOpData['verb']] = array
  159. (
  160. 'service_provider' => $oRestSP,
  161. 'description' => $aOpData['description'],
  162. );
  163. }
  164. }
  165. if (count($aOpToRestService) == 0)
  166. {
  167. throw new Exception("There is no service available for version '$sVersion'", RestResult::UNSUPPORTED_VERSION);
  168. }
  169. $sOperation = RestUtils::GetMandatoryParam($aJsonData, 'operation');
  170. if ($sOperation == 'list_operations')
  171. {
  172. $oResult = new RestResultListOperations();
  173. $oResult->message = "Operations: ".count($aOpToRestService);
  174. $oResult->version = $sVersion;
  175. foreach ($aOpToRestService as $sVerb => $aOpData)
  176. {
  177. $oResult->AddOperation($sVerb, $aOpData['description'], get_class($aOpData['service_provider']));
  178. }
  179. }
  180. else
  181. {
  182. if (!array_key_exists($sOperation, $aOpToRestService))
  183. {
  184. throw new Exception("Unknown verb '$sOperation' in version '$sVersion'", RestResult::UNKNOWN_OPERATION);
  185. }
  186. $oRS = $aOpToRestService[$sOperation]['service_provider'];
  187. $sProvider = get_class($oRS);
  188. CMDBObject::SetTrackOrigin('webservice-rest');
  189. $oResult = $oRS->ExecOperation($sVersion, $sOperation, $aJsonData);
  190. }
  191. }
  192. catch(Exception $e)
  193. {
  194. $oResult = new RestResult();
  195. if ($e->GetCode() == 0)
  196. {
  197. $oResult->code = RestResult::INTERNAL_ERROR;
  198. }
  199. else
  200. {
  201. $oResult->code = $e->GetCode();
  202. }
  203. $oResult->message = "Error: ".$e->GetMessage();
  204. }
  205. // Output the results
  206. //
  207. $sResponse = json_encode($oResult);
  208. if ($sResponse === false)
  209. {
  210. $oJsonIssue = new RestResult();
  211. $oJsonIssue->code = RestResult::INTERNAL_ERROR;
  212. $oJsonIssue->message = 'json encoding failed with message: '.json_last_error_msg().'. Full response structure for debugging purposes (print_r+bin2hex): '.bin2hex(print_r($oResult, true));
  213. $sResponse = json_encode($oJsonIssue);
  214. }
  215. $oP->add_header('Access-Control-Allow-Origin: *');
  216. $sCallback = utils::ReadParam('callback', null);
  217. if ($sCallback == null)
  218. {
  219. $oP->SetContentType('application/json');
  220. $oP->add($sResponse);
  221. }
  222. else
  223. {
  224. $oP->SetContentType('application/javascript');
  225. $oP->add($sCallback.'('.$sResponse.')');
  226. }
  227. $oP->Output();
  228. // Log usage
  229. //
  230. if (MetaModel::GetConfig()->Get('log_rest_service'))
  231. {
  232. $oLog = new EventRestService();
  233. $oLog->SetTrim('userinfo', UserRights::GetUser());
  234. $oLog->Set('version', $sVersion);
  235. $oLog->Set('operation', $sOperation);
  236. $oLog->SetTrim('json_input', $sJsonString);
  237. $oLog->Set('provider', $sProvider);
  238. $sMessage = $oResult->message;
  239. if (empty($oResult->message))
  240. {
  241. $sMessage = 'Ok';
  242. }
  243. $oLog->SetTrim('message', $sMessage);
  244. $oLog->Set('code', $oResult->code);
  245. $oLog->SetTrim('json_output', $sResponse);
  246. $oLog->DBInsertNoReload();
  247. }