1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program 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 General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22/** 23 * This class should be used to call API services locally using the CApiService classes. 24 */ 25class CLocalApiClient extends CApiClient { 26 27 /** 28 * Factory for creating API services. 29 * 30 * @var CRegistryFactory 31 */ 32 protected $serviceFactory; 33 34 /** 35 * Whether debug mode is enabled. 36 * 37 * @var bool 38 */ 39 protected $debug = false; 40 41 /** 42 * Set service factory. 43 * 44 * @param CRegistryFactory $factory 45 */ 46 public function setServiceFactory(CRegistryFactory $factory) { 47 $this->serviceFactory = $factory; 48 } 49 50 /** 51 * Call the given API service method and return the response. 52 * 53 * @param string $requestApi API name 54 * @param string $requestMethod API method 55 * @param array $params API parameters 56 * @param string $auth Authentication token 57 * 58 * @return CApiClientResponse 59 */ 60 public function callMethod($requestApi, $requestMethod, array $params, $auth) { 61 global $DB; 62 63 $api = strtolower($requestApi); 64 $method = strtolower($requestMethod); 65 66 $response = new CApiClientResponse(); 67 68 // check API 69 if (!$this->isValidApi($api)) { 70 $response->errorCode = ZBX_API_ERROR_NO_METHOD; 71 $response->errorMessage = _s('Incorrect API "%1$s".', $requestApi); 72 73 return $response; 74 } 75 76 // check method 77 if (!$this->isValidMethod($api, $method)) { 78 $response->errorCode = ZBX_API_ERROR_NO_METHOD; 79 $response->errorMessage = _s('Incorrect method "%1$s.%2$s".', $requestApi, $requestMethod); 80 81 return $response; 82 } 83 84 $requiresAuthentication = $this->requiresAuthentication($api, $method); 85 86 // check that no authentication token is passed to methods that don't require it 87 if (!$requiresAuthentication && $auth !== null) { 88 $response->errorCode = ZBX_API_ERROR_PARAMETERS; 89 $response->errorMessage = _s('The "%1$s.%2$s" method must be called without the "auth" parameter.', 90 $requestApi, $requestMethod 91 ); 92 93 return $response; 94 } 95 96 $newTransaction = false; 97 try { 98 // authenticate 99 if ($requiresAuthentication) { 100 $this->authenticate($auth); 101 } 102 103 // the nopermission parameter must not be available for external API calls. 104 unset($params['nopermissions']); 105 106 // if no transaction has been started yet - start one 107 if ($DB['TRANSACTIONS'] == 0) { 108 DBstart(); 109 $newTransaction = true; 110 } 111 112 // call API method 113 $result = call_user_func_array([$this->serviceFactory->getObject($api), $method], [$params]); 114 115 // if the method was called successfully - commit the transaction 116 if ($newTransaction) { 117 DBend(true); 118 } 119 120 $response->data = $result; 121 } 122 catch (Exception $e) { 123 if ($newTransaction) { 124 // if we're calling user.login and authentication failed - commit the transaction to save the 125 // failed attempt data 126 if ($api === 'user' && $method === 'login') { 127 DBend(true); 128 } 129 // otherwise - revert the transaction 130 else { 131 DBend(false); 132 } 133 } 134 135 $response->errorCode = ($e instanceof APIException) ? $e->getCode() : ZBX_API_ERROR_INTERNAL; 136 $response->errorMessage = $e->getMessage(); 137 138 // add debug data 139 if ($this->debug) { 140 $response->debug = $e->getTrace(); 141 } 142 } 143 144 return $response; 145 } 146 147 /** 148 * Checks if the authentication token is valid. 149 * 150 * @param string $auth 151 * 152 * @throws APIException 153 */ 154 protected function authenticate($auth) { 155 if (zbx_empty($auth)) { 156 throw new APIException(ZBX_API_ERROR_NO_AUTH, _('Not authorised.')); 157 } 158 159 $user = $this->serviceFactory->getObject('user')->checkAuthentication(['sessionid' => $auth]); 160 $this->debug = $user['debug_mode']; 161 } 162 163 /** 164 * Returns true if the given API is valid. 165 * 166 * @param string $api 167 * 168 * @return bool 169 */ 170 protected function isValidApi($api) { 171 return $this->serviceFactory->hasObject($api); 172 } 173 174 /** 175 * Returns true if the given method is valid. 176 * 177 * @param string $api 178 * @param string $method 179 * 180 * @return bool 181 */ 182 protected function isValidMethod($api, $method) { 183 $apiService = $this->serviceFactory->getObject($api); 184 185 // validate the method 186 $availableMethods = []; 187 foreach (get_class_methods($apiService) as $serviceMethod) { 188 // the comparison must be case insensitive 189 $availableMethods[strtolower($serviceMethod)] = true; 190 } 191 192 return isset($availableMethods[$method]); 193 } 194 195 /** 196 * Returns true if calling the given method requires a valid authentication token. 197 * 198 * @param $api 199 * @param $method 200 * 201 * @return bool 202 */ 203 protected function requiresAuthentication($api, $method) { 204 return !(($api === 'user' && $method === 'login') 205 || ($api === 'user' && $method === 'checkauthentication') 206 || ($api === 'apiinfo' && $method === 'version')); 207 } 208} 209