Api.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. <?php
  2. # Copyright (c) 2013-2016, OVH SAS.
  3. # All rights reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions are met:
  7. #
  8. # * Redistributions of source code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. # * Redistributions in binary form must reproduce the above copyright
  11. # notice, this list of conditions and the following disclaimer in the
  12. # documentation and/or other materials provided with the distribution.
  13. # * Neither the name of OVH SAS nor the
  14. # names of its contributors may be used to endorse or promote products
  15. # derived from this software without specific prior written permission.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY OVH SAS AND CONTRIBUTORS ``AS IS'' AND ANY
  18. # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. # DISCLAIMED. IN NO EVENT SHALL OVH SAS AND CONTRIBUTORS BE LIABLE FOR ANY
  21. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  26. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. /**
  28. * This file contains code about \Ovh\Api class
  29. */
  30. namespace Ovh;
  31. use GuzzleHttp\Client;
  32. use GuzzleHttp\Psr7\Request;
  33. use GuzzleHttp\Psr7\Response;
  34. /**
  35. * Wrapper to manage login and exchanges with simpliest Ovh API
  36. *
  37. * This class manage how works connections to the simple Ovh API with
  38. * login method and call wrapper
  39. * Http connections use guzzle http client api and result of request are
  40. * object from this http wrapper
  41. *
  42. * @package Ovh
  43. * @category Ovh
  44. */
  45. class Api
  46. {
  47. /**
  48. * Url to communicate with Ovh API
  49. *
  50. * @var array
  51. */
  52. private $endpoints = [
  53. 'ovh-eu' => 'https://api.ovh.com/1.0',
  54. 'ovh-ca' => 'https://ca.api.ovh.com/1.0',
  55. 'kimsufi-eu' => 'https://eu.api.kimsufi.com/1.0',
  56. 'kimsufi-ca' => 'https://ca.api.kimsufi.com/1.0',
  57. 'soyoustart-eu' => 'https://eu.api.soyoustart.com/1.0',
  58. 'soyoustart-ca' => 'https://ca.api.soyoustart.com/1.0',
  59. 'runabove-ca' => 'https://api.runabove.com/1.0',
  60. ];
  61. /**
  62. * Contain endpoint selected to choose API
  63. *
  64. * @var string
  65. */
  66. private $endpoint = null;
  67. /**
  68. * Contain key of the current application
  69. *
  70. * @var string
  71. */
  72. private $application_key = null;
  73. /**
  74. * Contain secret of the current application
  75. *
  76. * @var string
  77. */
  78. private $application_secret = null;
  79. /**
  80. * Contain consumer key of the current application
  81. *
  82. * @var string
  83. */
  84. private $consumer_key = null;
  85. /**
  86. * Contain delta between local timestamp and api server timestamp
  87. *
  88. * @var string
  89. */
  90. private $time_delta = null;
  91. /**
  92. * Contain http client connection
  93. *
  94. * @var Client
  95. */
  96. private $http_client = null;
  97. /**
  98. * Construct a new wrapper instance
  99. *
  100. * @param string $application_key key of your application.
  101. * For OVH APIs, you can create a application's credentials on
  102. * https://api.ovh.com/createApp/
  103. * @param string $application_secret secret of your application.
  104. * @param string $api_endpoint name of api selected
  105. * @param string $consumer_key If you have already a consumer key, this parameter prevent to do a
  106. * new authentication
  107. * @param Client $http_client instance of http client
  108. *
  109. * @throws Exceptions\InvalidParameterException if one parameter is missing or with bad value
  110. */
  111. public function __construct(
  112. $application_key,
  113. $application_secret,
  114. $api_endpoint,
  115. $consumer_key = null,
  116. Client $http_client = null
  117. ) {
  118. if (!isset($application_key)) {
  119. throw new Exceptions\InvalidParameterException("Application key parameter is empty");
  120. }
  121. if (!isset($application_secret)) {
  122. throw new Exceptions\InvalidParameterException("Application secret parameter is empty");
  123. }
  124. if (!isset($api_endpoint)) {
  125. throw new Exceptions\InvalidParameterException("Endpoint parameter is empty");
  126. }
  127. if (!array_key_exists($api_endpoint, $this->endpoints)) {
  128. throw new Exceptions\InvalidParameterException("Unknown provided endpoint");
  129. }
  130. if (!isset($http_client)) {
  131. $http_client = new Client([
  132. 'timeout' => 30,
  133. 'connect_timeout' => 5,
  134. ]);
  135. }
  136. $this->application_key = $application_key;
  137. $this->endpoint = $this->endpoints[$api_endpoint];
  138. $this->application_secret = $application_secret;
  139. $this->http_client = $http_client;
  140. $this->consumer_key = $consumer_key;
  141. $this->time_delta = null;
  142. }
  143. /**
  144. * Calculate time delta between local machine and API's server
  145. *
  146. * @throws \GuzzleHttp\Exception\ClientException if http request is an error
  147. * @return int
  148. */
  149. private function calculateTimeDelta()
  150. {
  151. if (!isset($this->time_delta)) {
  152. $response = $this->http_client->get($this->endpoint . "/auth/time");
  153. $serverTimestamp = (int)(String)$response->getBody();
  154. $this->time_delta = $serverTimestamp - (int)\time();
  155. }
  156. return $this->time_delta;
  157. }
  158. /**
  159. * Request a consumer key from the API and the validation link to
  160. * authorize user to validate this consumer key
  161. *
  162. * @param array $accessRules list of rules your application need.
  163. * @param string $redirection url to redirect on your website after authentication
  164. *
  165. * @return mixed
  166. * @throws \GuzzleHttp\Exception\ClientException if http request is an error
  167. */
  168. public function requestCredentials(
  169. array $accessRules,
  170. $redirection = null
  171. ) {
  172. $parameters = new \StdClass();
  173. $parameters->accessRules = $accessRules;
  174. $parameters->redirection = $redirection;
  175. //bypass authentication for this call
  176. $response = $this->rawCall(
  177. 'POST',
  178. '/auth/credential',
  179. $parameters,
  180. false
  181. );
  182. $this->consumer_key = $response["consumerKey"];
  183. return $response;
  184. }
  185. /**
  186. * This is the main method of this wrapper. It will
  187. * sign a given query and return its result.
  188. *
  189. * @param string $method HTTP method of request (GET,POST,PUT,DELETE)
  190. * @param string $path relative url of API request
  191. * @param \stdClass|array|null $content body of the request
  192. * @param bool $is_authenticated if the request use authentication
  193. *
  194. * @return array
  195. * @throws \GuzzleHttp\Exception\ClientException if http request is an error
  196. */
  197. private function rawCall($method, $path, $content = null, $is_authenticated = true)
  198. {
  199. $url = $this->endpoint . $path;
  200. $request = new Request($method, $url);
  201. if (isset($content) && $method == 'GET') {
  202. $query_string = $request->getUri()->getQuery();
  203. $query = array();
  204. if (!empty($query_string)) {
  205. $queries = explode('&', $query_string);
  206. foreach($queries as $element) {
  207. $key_value_query = explode('=', $element, 2);
  208. $query[$key_value_query[0]] = $key_value_query[1];
  209. }
  210. }
  211. $query = array_merge($query, (array)$content);
  212. // rewrite query args to properly dump true/false parameters
  213. foreach($query as $key => $value)
  214. {
  215. if ($value === false)
  216. {
  217. $query[$key] = "false";
  218. }
  219. elseif ($value === true)
  220. {
  221. $query[$key] = "true";
  222. }
  223. }
  224. $query = \GuzzleHttp\Psr7\build_query($query);
  225. $url = $request->getUri()->withQuery($query);
  226. $request = $request->withUri($url);
  227. $body = "";
  228. } elseif (isset($content)) {
  229. $body = json_encode($content);
  230. $request->getBody()->write($body);
  231. } else {
  232. $body = "";
  233. }
  234. $headers = [
  235. 'Content-Type' => 'application/json; charset=utf-8',
  236. 'X-Ovh-Application' => $this->application_key,
  237. ];
  238. if ($is_authenticated) {
  239. if (!isset($this->time_delta)) {
  240. $this->calculateTimeDelta();
  241. }
  242. $now = time() + $this->time_delta;
  243. $headers['X-Ovh-Timestamp'] = $now;
  244. if (isset($this->consumer_key)) {
  245. $toSign = $this->application_secret . '+' . $this->consumer_key . '+' . $method
  246. . '+' . $url . '+' . $body . '+' . $now;
  247. $signature = '$1$' . sha1($toSign);
  248. $headers['X-Ovh-Consumer'] = $this->consumer_key;
  249. $headers['X-Ovh-Signature'] = $signature;
  250. }
  251. }
  252. /** @var Response $response */
  253. $response = $this->http_client->send($request, ['headers' => $headers]);
  254. return json_decode($response->getBody(), true);
  255. }
  256. /**
  257. * Wrap call to Ovh APIs for GET requests
  258. *
  259. * @param string $path path ask inside api
  260. * @param array $content content to send inside body of request
  261. *
  262. * @return array
  263. * @throws \GuzzleHttp\Exception\ClientException if http request is an error
  264. */
  265. public function get($path, $content = null)
  266. {
  267. return $this->rawCall("GET", $path, $content);
  268. }
  269. /**
  270. * Wrap call to Ovh APIs for POST requests
  271. *
  272. * @param string $path path ask inside api
  273. * @param array $content content to send inside body of request
  274. *
  275. * @return array
  276. * @throws \GuzzleHttp\Exception\ClientException if http request is an error
  277. */
  278. public function post($path, $content = null)
  279. {
  280. return $this->rawCall("POST", $path, $content);
  281. }
  282. /**
  283. * Wrap call to Ovh APIs for PUT requests
  284. *
  285. * @param string $path path ask inside api
  286. * @param array $content content to send inside body of request
  287. *
  288. * @return array
  289. * @throws \GuzzleHttp\Exception\ClientException if http request is an error
  290. */
  291. public function put($path, $content)
  292. {
  293. return $this->rawCall("PUT", $path, $content);
  294. }
  295. /**
  296. * Wrap call to Ovh APIs for DELETE requests
  297. *
  298. * @param string $path path ask inside api
  299. * @param array $content content to send inside body of request
  300. *
  301. * @return array
  302. * @throws \GuzzleHttp\Exception\ClientException if http request is an error
  303. */
  304. public function delete($path, $content = null)
  305. {
  306. return $this->rawCall("DELETE", $path, $content);
  307. }
  308. /**
  309. * Get the current consumer key
  310. */
  311. public function getConsumerKey()
  312. {
  313. return $this->consumer_key;
  314. }
  315. /**
  316. * Return instance of http client
  317. */
  318. public function getHttpClient()
  319. {
  320. return $this->http_client;
  321. }
  322. }