PageRenderTime 24ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/branches/1.2.0/src/api/class.ScalrAPICore.php

http://scalr.googlecode.com/
PHP | 266 lines | 239 code | 16 blank | 11 comment | 7 complexity | c68f1a309cbc2e86a2614b31c348bb80 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, GPL-3.0
  1. <?php
  2. define("API_SERVER_IP", "174.132.108.66");
  3. abstract class ScalrAPICore
  4. {
  5. const HASH_ALGO = 'SHA256';
  6. protected $Request;
  7. protected $Client;
  8. protected $DB;
  9. protected $Logger;
  10. public $Version;
  11. protected $LastTransactionID;
  12. function __construct($version)
  13. {
  14. $this->DB = Core::GetDBInstance();
  15. $this->Logger = Logger::getLogger(__CLASS__);
  16. $this->Version = $version;
  17. }
  18. private function AuthenticateREST($request)
  19. {
  20. if (!$request['Signature'])
  21. throw new Exception("Signature is missing");
  22. if (!$request['KeyID'])
  23. throw new Exception("KeyID is missing");
  24. if (!$request['Timestamp'])
  25. throw new Exception("Timestamp is missing");
  26. //TODO: Validate TimeStamp
  27. ksort($request);
  28. $string_to_sign = "";
  29. foreach ($request as $k=>$v)
  30. {
  31. if (!in_array($k, array("Signature")))
  32. $string_to_sign.= "{$k}{$v}";
  33. }
  34. $this->Client = Client::LoadByScalrKeyID($request['KeyID']);
  35. $auth_key = $this->Client->GetScalrAPIKey();
  36. $valid_sign = base64_encode(hash_hmac(self::HASH_ALGO, $string_to_sign, $auth_key, 1));
  37. if ($valid_sign != $request['Signature'])
  38. throw new Exception("Signature doesn't match");
  39. }
  40. public function BuildRestServer($request)
  41. {
  42. try
  43. {
  44. $Reflect = new ReflectionObject($this);
  45. if ($Reflect->hasMethod($request['Action']))
  46. {
  47. //Authenticate
  48. $this->AuthenticateREST($request);
  49. if ($this->Client->GetSettingValue(CLIENT_SETTINGS::API_ENABLED) != 1)
  50. throw new Exception(_("API disabled for you. You can enable it at 'Settings -> System settings'"));
  51. //Check IP Addresses
  52. $ips = explode(",", $this->Client->GetSettingValue(CLIENT_SETTINGS::API_ALLOWED_IPS));
  53. if (!$this->IPAccessCheck($ips) && $_SERVER['REMOTE_ADDR'] != API_SERVER_IP)
  54. throw new Exception(sprintf(_("Access to the API is not allowed from your IP '%s'"), $_SERVER['REMOTE_ADDR']));
  55. //Execute API call
  56. $ReflectMethod = $Reflect->getMethod($request['Action']);
  57. $args = array();
  58. foreach ($ReflectMethod->getParameters() as $param)
  59. {
  60. if (!$param->isOptional() && !isset($request[$param->getName()]))
  61. throw new Exception(sprintf("Missing required parameter '%s'", $param->getName()));
  62. else
  63. $args[$param->getName()] = $request[$param->getName()];
  64. }
  65. $result = $ReflectMethod->invokeArgs($this, $args);
  66. // Create response
  67. $DOMDocument = new DOMDocument('1.0', 'UTF-8');
  68. $DOMDocument->loadXML("<{$request['Action']}Response></{$request['Action']}Response>");
  69. $this->ObjectToXML($result, $DOMDocument->documentElement, $DOMDocument);
  70. $retval = $DOMDocument->saveXML();
  71. }
  72. else
  73. throw new Exception(sprintf("Action '%s' is not defined", $request['Action']));
  74. }
  75. catch(Exception $e)
  76. {
  77. if (!$this->LastTransactionID)
  78. $this->LastTransactionID = $this->GenerateTransactionID();
  79. $retval = "<?xml version=\"1.0\"?>\n".
  80. "<Error>\n".
  81. "\t<TransactionID>{$this->LastTransactionID}</TransactionID>\n".
  82. "\t<Message>{$e->getMessage()}</Message>\n".
  83. "</Error>\n";
  84. }
  85. $this->LogRequest(
  86. $this->LastTransactionID,
  87. $request['Action'],
  88. (($_SERVER['REMOTE_ADDR'] == API_SERVER_IP) ? 'Mobile app' : $_SERVER['REMOTE_ADDR']),
  89. $request,
  90. $retval
  91. );
  92. header("Content-type: text/xml");
  93. header("Content-length: ".strlen($retval));
  94. print $retval;
  95. }
  96. protected function LogRequest($trans_id, $action, $ipaddr, $request, $response)
  97. {
  98. try
  99. {
  100. $this->DB->Execute("INSERT INTO api_log SET
  101. transaction_id = ?,
  102. dtadded = ?,
  103. action = ?,
  104. ipaddress = ?,
  105. request = ?,
  106. response = ?,
  107. clientid = ?
  108. ",array(
  109. $trans_id,
  110. time(),
  111. $action,
  112. $ipaddr,
  113. http_build_query($request),
  114. $response,
  115. $this->Client->ID
  116. ));
  117. }
  118. catch(Exception $e) {}
  119. }
  120. protected function IPAccessCheck($allowed_ips)
  121. {
  122. $current_ip = $_SERVER['REMOTE_ADDR'];
  123. $current_ip_parts = explode(".", $current_ip);
  124. foreach ($allowed_ips as $allowed_ip)
  125. {
  126. $allowedhost = trim($allowed_ip);
  127. if ($allowedhost == '')
  128. continue;
  129. if (preg_match("/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/si", $allowedhost))
  130. {
  131. if (ip2long($allowedhost) == ip2long($current_ip))
  132. return true;
  133. }
  134. elseif (stristr($allowedhost, "*"))
  135. {
  136. $ip_parts = explode(".", trim($allowedhost));
  137. if (
  138. ($ip_parts[0] == "*" || $ip_parts[0] == $current_ip_parts[0]) &&
  139. ($ip_parts[1] == "*" || $ip_parts[1] == $current_ip_parts[1]) &&
  140. ($ip_parts[2] == "*" || $ip_parts[2] == $current_ip_parts[2]) &&
  141. ($ip_parts[3] == "*" || $ip_parts[3] == $current_ip_parts[3])
  142. )
  143. return true;
  144. }
  145. else
  146. {
  147. $ip = @gethostbyname($allowedhost);
  148. if ($ip != $allowedhost)
  149. {
  150. if (ip2long($ip) == ip2long($current_ip))
  151. return true;
  152. }
  153. }
  154. }
  155. return false;
  156. }
  157. protected function ObjectToXML($obj, &$DOMElement, &$DOMDocument)
  158. {
  159. if (is_object($obj) || is_array($obj))
  160. {
  161. foreach ($obj as $k=>$v)
  162. {
  163. if (is_object($v))
  164. $this->ObjectToXML($v, $DOMElement->appendChild($DOMDocument->createElement($k)), $DOMDocument);
  165. elseif (is_array($v))
  166. foreach ($v as $vv)
  167. {
  168. $e = &$DOMElement->appendChild($DOMDocument->createElement($k));
  169. $this->ObjectToXML($vv, $e, $DOMDocument);
  170. }
  171. else
  172. $DOMElement->appendChild($DOMDocument->createElement($k, $v));
  173. }
  174. }
  175. else
  176. $DOMElement->appendChild($DOMDocument->createTextNode($obj));
  177. }
  178. protected function CreateInitialResponse()
  179. {
  180. $response = new stdClass();
  181. $response->{"TransactionID"} = $this->GenerateTransactionID();
  182. return $response;
  183. }
  184. private function GenerateTransactionID()
  185. {
  186. $pr_bits = false;
  187. if (is_a ( $this, 'uuid' )) {
  188. if (is_resource ( $this->urand )) {
  189. $pr_bits .= @fread ( $this->urand, 16 );
  190. }
  191. }
  192. if (! $pr_bits) {
  193. $fp = @fopen ( '/dev/urandom', 'rb' );
  194. if ($fp !== false) {
  195. $pr_bits .= @fread ( $fp, 16 );
  196. @fclose ( $fp );
  197. } else {
  198. // If /dev/urandom isn't available (eg: in non-unix systems), use mt_rand().
  199. $pr_bits = "";
  200. for($cnt = 0; $cnt < 16; $cnt ++) {
  201. $pr_bits .= chr ( mt_rand ( 0, 255 ) );
  202. }
  203. }
  204. }
  205. $time_low = bin2hex ( substr ( $pr_bits, 0, 4 ) );
  206. $time_mid = bin2hex ( substr ( $pr_bits, 4, 2 ) );
  207. $time_hi_and_version = bin2hex ( substr ( $pr_bits, 6, 2 ) );
  208. $clock_seq_hi_and_reserved = bin2hex ( substr ( $pr_bits, 8, 2 ) );
  209. $node = bin2hex ( substr ( $pr_bits, 10, 6 ) );
  210. /**
  211. * Set the four most significant bits (bits 12 through 15) of the
  212. * time_hi_and_version field to the 4-bit version number from
  213. * Section 4.1.3.
  214. * @see http://tools.ietf.org/html/rfc4122#section-4.1.3
  215. */
  216. $time_hi_and_version = hexdec ( $time_hi_and_version );
  217. $time_hi_and_version = $time_hi_and_version >> 4;
  218. $time_hi_and_version = $time_hi_and_version | 0x4000;
  219. /**
  220. * Set the two most significant bits (bits 6 and 7) of the
  221. * clock_seq_hi_and_reserved to zero and one, respectively.
  222. */
  223. $clock_seq_hi_and_reserved = hexdec ( $clock_seq_hi_and_reserved );
  224. $clock_seq_hi_and_reserved = $clock_seq_hi_and_reserved >> 2;
  225. $clock_seq_hi_and_reserved = $clock_seq_hi_and_reserved | 0x8000;
  226. $this->LastTransactionID = sprintf ( '%08s-%04s-%04x-%04x-%012s', $time_low, $time_mid, $time_hi_and_version, $clock_seq_hi_and_reserved, $node );
  227. return $this->LastTransactionID;
  228. }
  229. }
  230. ?>