AbstractSignature.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. <?php
  2. /**
  3. * Copyright 2019 Huawei Technologies Co.,Ltd.
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  5. * this file except in compliance with the License. You may obtain a copy of the
  6. * License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software distributed
  11. * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  12. * CONDITIONS OF ANY KIND, either express or implied. See the License for the
  13. * specific language governing permissions and limitations under the License.
  14. *
  15. */
  16. namespace Obs\Internal\Signature;
  17. use Obs\Log\ObsLog;
  18. use Obs\Internal\Resource\Constants;
  19. use Obs\ObsException;
  20. use Obs\Internal\Common\SchemaFormatter;
  21. use GuzzleHttp\Psr7\Stream;
  22. use Obs\Internal\Common\Model;
  23. use Psr\Http\Message\StreamInterface;
  24. use Obs\Internal\Common\ObsTransform;
  25. use Obs\Internal\Common\V2Transform;
  26. abstract class AbstractSignature implements SignatureInterface
  27. {
  28. protected $ak;
  29. protected $sk;
  30. protected $pathStyle;
  31. protected $endpoint;
  32. protected $methodName;
  33. protected $securityToken;
  34. protected $signature;
  35. protected $isCname;
  36. public static function urlencodeWithSafe($val, $safe='/'){
  37. if(($len = strlen($val)) === 0){
  38. return '';
  39. }
  40. $buffer = [];
  41. for ($index=0;$index<$len;$index++){
  42. $str = $val[$index];
  43. $buffer[] = !($pos = strpos($safe, $str)) && $pos !== 0 ? rawurlencode($str) : $str;
  44. }
  45. return implode('', $buffer);
  46. }
  47. protected function __construct($ak, $sk, $pathStyle, $endpoint, $methodName, $signature, $securityToken=false, $isCname=false)
  48. {
  49. $this -> ak = $ak;
  50. $this -> sk = $sk;
  51. $this -> pathStyle = $pathStyle;
  52. $this -> endpoint = $endpoint;
  53. $this -> methodName = $methodName;
  54. $this -> signature = $signature;
  55. $this -> securityToken = $securityToken;
  56. $this -> isCname = $isCname;
  57. }
  58. protected function transXmlByType($key, &$value, &$subParams, $transHolder)
  59. {
  60. $xml = [];
  61. $treatAsString = false;
  62. if(isset($value['type'])){
  63. $type = $value['type'];
  64. if($type === 'array'){
  65. $name = isset($value['sentAs']) ? $value['sentAs'] : $key;
  66. $subXml = [];
  67. foreach($subParams as $item){
  68. $temp = $this->transXmlByType($key, $value['items'], $item, $transHolder);
  69. if($temp !== ''){
  70. $subXml[] = $temp;
  71. }
  72. }
  73. if(!empty($subXml)){
  74. if(!isset($value['data']['xmlFlattened'])){
  75. $xml[] = '<' . $name . '>';
  76. $xml[] = implode('', $subXml);
  77. $xml[] = '</' . $name . '>';
  78. }else{
  79. $xml[] = implode('', $subXml);
  80. }
  81. }
  82. }else if($type === 'object'){
  83. $name = isset($value['sentAs']) ? $value['sentAs'] : (isset($value['name']) ? $value['name'] : $key);
  84. $properties = $value['properties'];
  85. $subXml = [];
  86. $attr = [];
  87. foreach ($properties as $pkey => $pvalue){
  88. if(isset($pvalue['required']) && $pvalue['required'] && !isset($subParams[$pkey])){
  89. $obsException= new ObsException('param:' .$pkey. ' is required');
  90. $obsException-> setExceptionType('client');
  91. throw $obsException;
  92. }
  93. if(isset($subParams[$pkey])){
  94. if(isset($pvalue['data']) && isset($pvalue['data']['xmlAttribute']) && $pvalue['data']['xmlAttribute']){
  95. $attrValue = $this->xml_tansfer(trim(strval($subParams[$pkey])));
  96. $attr[$pvalue['sentAs']] = '"' . $attrValue . '"';
  97. if(isset($pvalue['data']['xmlNamespace'])){
  98. $ns = substr($pvalue['sentAs'], 0, strpos($pvalue['sentAs'], ':'));
  99. $attr['xmlns:' . $ns] = '"' . $pvalue['data']['xmlNamespace'] . '"';
  100. }
  101. }else{
  102. $subXml[] = $this -> transXmlByType($pkey, $pvalue, $subParams[$pkey], $transHolder);
  103. }
  104. }
  105. }
  106. $val = implode('', $subXml);
  107. if($val !== ''){
  108. $_name = $name;
  109. if(!empty($attr)){
  110. foreach ($attr as $akey => $avalue){
  111. $_name .= ' ' . $akey . '=' . $avalue;
  112. }
  113. }
  114. if(!isset($value['data']['xmlFlattened'])){
  115. $xml[] = '<' . $_name . '>';
  116. $xml[] = $val;
  117. $xml[] = '</' . $name . '>';
  118. } else {
  119. $xml[] = $val;
  120. }
  121. }
  122. }else{
  123. $treatAsString = true;
  124. }
  125. }else{
  126. $treatAsString = true;
  127. $type = null;
  128. }
  129. if($treatAsString){
  130. if($type === 'boolean'){
  131. if(!is_bool($subParams) && strval($subParams) !== 'false' && strval($subParams) !== 'true'){
  132. $obsException= new ObsException('param:' .$key. ' is not a boolean value');
  133. $obsException-> setExceptionType('client');
  134. throw $obsException;
  135. }
  136. }else if($type === 'numeric'){
  137. if(!is_numeric($subParams)){
  138. $obsException= new ObsException('param:' .$key. ' is not a numeric value');
  139. $obsException-> setExceptionType('client');
  140. throw $obsException;
  141. }
  142. }else if($type === 'float'){
  143. if(!is_float($subParams)){
  144. $obsException= new ObsException('param:' .$key. ' is not a float value');
  145. $obsException-> setExceptionType('client');
  146. throw $obsException;
  147. }
  148. }else if($type === 'int' || $type === 'integer'){
  149. if(!is_int($subParams)){
  150. $obsException= new ObsException('param:' .$key. ' is not a int value');
  151. $obsException-> setExceptionType('client');
  152. throw $obsException;
  153. }
  154. }
  155. $name = isset($value['sentAs']) ? $value['sentAs'] : $key;
  156. if(is_bool($subParams)){
  157. $val = $subParams ? 'true' : 'false';
  158. }else{
  159. $val = strval($subParams);
  160. }
  161. if(isset($value['format'])){
  162. $val = SchemaFormatter::format($value['format'], $val);
  163. }
  164. if (isset($value['transform'])) {
  165. $val = $transHolder->transform($value['transform'], $val);
  166. }
  167. if(isset($val) && $val !== ''){
  168. $val = $this->xml_tansfer($val);
  169. if(!isset($value['data']['xmlFlattened'])){
  170. $xml[] = '<' . $name . '>';
  171. $xml[] = $val;
  172. $xml[] = '</' . $name . '>';
  173. } else {
  174. $xml[] = $val;
  175. }
  176. }else if(isset($value['canEmpty']) && $value['canEmpty']){
  177. $xml[] = '<' . $name . '>';
  178. $xml[] = $val;
  179. $xml[] = '</' . $name . '>';
  180. }
  181. }
  182. $ret = implode('', $xml);
  183. if(isset($value['wrapper'])){
  184. $ret = '<'. $value['wrapper'] . '>' . $ret . '</'. $value['wrapper'] . '>';
  185. }
  186. return $ret;
  187. }
  188. private function xml_tansfer($tag) {
  189. $search = array('&', '<', '>', '\'', '"');
  190. $repalce = array('&amp;', '&lt;', '&gt;', '&apos;', '&quot;');
  191. $transferXml = str_replace($search, $repalce, $tag);
  192. return $transferXml;
  193. }
  194. protected function prepareAuth(array &$requestConfig, array &$params, Model $model)
  195. {
  196. $transHolder = strcasecmp($this-> signature, 'obs') === 0 ? ObsTransform::getInstance() : V2Transform::getInstance();
  197. $method = $requestConfig['httpMethod'];
  198. $requestUrl = $this->endpoint;
  199. $headers = [];
  200. $pathArgs = [];
  201. $dnsParam = null;
  202. $uriParam = null;
  203. $body = [];
  204. $xml = [];
  205. if(isset($requestConfig['specialParam'])){
  206. $pathArgs[$requestConfig['specialParam']] = '';
  207. }
  208. $result = ['body' => null];
  209. $url = parse_url($requestUrl);
  210. $host = $url['host'];
  211. $fileFlag = false;
  212. if(isset($requestConfig['requestParameters'])){
  213. $paramsMetadata = $requestConfig['requestParameters'];
  214. foreach ($paramsMetadata as $key => $value){
  215. if(isset($value['required']) && $value['required'] && !isset($params[$key])){
  216. $obsException= new ObsException('param:' .$key. ' is required');
  217. $obsException-> setExceptionType('client');
  218. throw $obsException;
  219. }
  220. if(isset($params[$key]) && isset($value['location'])){
  221. $location = $value['location'];
  222. $val = $params[$key];
  223. $type = 'string';
  224. if($val !== '' && isset($value['type'])){
  225. $type = $value['type'];
  226. if($type === 'boolean'){
  227. if(!is_bool($val) && strval($val) !== 'false' && strval($val) !== 'true'){
  228. $obsException= new ObsException('param:' .$key. ' is not a boolean value');
  229. $obsException-> setExceptionType('client');
  230. throw $obsException;
  231. }
  232. }else if($type === 'numeric'){
  233. if(!is_numeric($val)){
  234. $obsException= new ObsException('param:' .$key. ' is not a numeric value');
  235. $obsException-> setExceptionType('client');
  236. throw $obsException;
  237. }
  238. }else if($type === 'float'){
  239. if(!is_float($val)){
  240. $obsException= new ObsException('param:' .$key. ' is not a float value');
  241. $obsException-> setExceptionType('client');
  242. throw $obsException;
  243. }
  244. }else if($type === 'int' || $type === 'integer'){
  245. if(!is_int($val)){
  246. $obsException= new ObsException('param:' .$key. ' is not a int value');
  247. $obsException-> setExceptionType('client');
  248. throw $obsException;
  249. }
  250. }
  251. }
  252. if($location === 'header'){
  253. if($type === 'object'){
  254. if(is_array($val)){
  255. $sentAs = strtolower($value['sentAs']);
  256. foreach ($val as $k => $v){
  257. $k = self::urlencodeWithSafe(strtolower($k), ' ;/?:@&=+$,');
  258. $name = strpos($k, $sentAs) === 0 ? $k : $sentAs . $k;
  259. $headers[$name] = self::urlencodeWithSafe($v, ' ;/?:@&=+$,\'*');
  260. }
  261. }
  262. }else if($type === 'array'){
  263. if(is_array($val)){
  264. $name = isset($value['sentAs']) ? $value['sentAs'] : (isset($value['items']['sentAs']) ? $value['items']['sentAs'] : $key);
  265. $temp = [];
  266. foreach ($val as $v){
  267. if(($v = strval($v)) !== ''){
  268. $temp[] = self::urlencodeWithSafe($val, ' ;/?:@&=+$,\'*');
  269. }
  270. }
  271. $headers[$name] = $temp;
  272. }
  273. }else if($type === 'password'){
  274. if(($val = strval($val)) !== ''){
  275. $name = isset($value['sentAs']) ? $value['sentAs'] : $key;
  276. $pwdName = isset($value['pwdSentAs']) ? $value['pwdSentAs'] : $name . '-MD5';
  277. $val1 = base64_encode($val);
  278. $val2 = base64_encode(md5($val, true));
  279. $headers[$name] = $val1;
  280. $headers[$pwdName] = $val2;
  281. }
  282. }else{
  283. if (isset($value['transform'])) {
  284. $val = $transHolder->transform($value['transform'], strval($val));
  285. }
  286. if(isset($val)){
  287. if(is_bool($val)){
  288. $val = $val ? 'true' : 'false';
  289. }else{
  290. $val = strval($val);
  291. }
  292. if($val !== ''){
  293. $name = isset($value['sentAs']) ? $value['sentAs'] : $key;
  294. if(isset($value['format'])){
  295. $val = SchemaFormatter::format($value['format'], $val);
  296. }
  297. $headers[$name] = self::urlencodeWithSafe($val, ' ;/?:@&=+$,\'*');
  298. }
  299. }
  300. }
  301. }else if($location === 'uri' && $uriParam === null){
  302. $uriParam = self::urlencodeWithSafe($val);
  303. }else if($location === 'dns' && $dnsParam === null){
  304. $dnsParam = $val;
  305. }else if($location === 'query'){
  306. $name = isset($value['sentAs']) ? $value['sentAs'] : $key;
  307. if(strval($val) !== ''){
  308. if (strcasecmp ( $this->signature, 'v4' ) === 0) {
  309. $pathArgs[rawurlencode($name)] = rawurlencode(strval($val));
  310. } else {
  311. $pathArgs[self::urlencodeWithSafe($name)] = self::urlencodeWithSafe(strval($val));
  312. }
  313. }
  314. }else if($location === 'xml'){
  315. $val = $this->transXmlByType($key, $value, $val, $transHolder);
  316. if($val !== ''){
  317. $xml[] = $val;
  318. }
  319. }else if($location === 'body'){
  320. if(isset($result['body'])){
  321. $obsException= new ObsException('duplicated body provided');
  322. $obsException-> setExceptionType('client');
  323. throw $obsException;
  324. }
  325. if($type === 'file'){
  326. if(!file_exists($val)){
  327. $obsException= new ObsException('file[' .$val. '] does not exist');
  328. $obsException-> setExceptionType('client');
  329. throw $obsException;
  330. }
  331. $result['body'] = new Stream(fopen($val, 'r'));
  332. $fileFlag = true;
  333. }else if($type === 'stream'){
  334. $result['body'] = $val;
  335. } else if ($type === 'json') {
  336. //TODO
  337. $jsonData = json_encode($val);
  338. if (!$jsonData) {
  339. $obsException= new ObsException('input is invalid, since it is not json data');
  340. $obsException-> setExceptionType('client');
  341. throw $obsException;
  342. }
  343. $result['body'] = strval($jsonData);
  344. } else{
  345. $result['body'] = strval($val);
  346. }
  347. }else if($location === 'response'){
  348. $model[$key] = ['value' => $val, 'type' => $type];
  349. }
  350. }
  351. }
  352. if($dnsParam){
  353. if($this -> pathStyle){
  354. $requestUrl = $requestUrl . '/' . $dnsParam;
  355. }else{
  356. $defaultPort = strtolower($url['scheme']) === 'https' ? '443' : '80';
  357. $host = $this -> isCname ? $host : $dnsParam. '.' . $host;
  358. $requestUrl = $url['scheme'] . '://' . $host . ':' . (isset($url['port']) ? $url['port'] : $defaultPort);
  359. }
  360. }
  361. if($uriParam){
  362. $requestUrl = $requestUrl . '/' . $uriParam;
  363. }
  364. if(!empty($pathArgs)){
  365. $requestUrl .= '?';
  366. $_pathArgs = [];
  367. foreach ($pathArgs as $key => $value){
  368. $_pathArgs[] = $value === null || $value === '' ? $key : $key . '=' . $value;
  369. }
  370. $requestUrl .= implode('&', $_pathArgs);
  371. }
  372. }
  373. if($xml || (isset($requestConfig['data']['xmlAllowEmpty']) && $requestConfig['data']['xmlAllowEmpty'])){
  374. $body[] = '<';
  375. $xmlRoot = $requestConfig['data']['xmlRoot']['name'];
  376. $body[] = $xmlRoot;
  377. $body[] = '>';
  378. $body[] = implode('', $xml);
  379. $body[] = '</';
  380. $body[] = $xmlRoot;
  381. $body[] = '>';
  382. $headers['Content-Type'] = 'application/xml';
  383. $result['body'] = implode('', $body);
  384. ObsLog::commonLog(DEBUG, 'request content ' . $result['body']);
  385. if(isset($requestConfig['data']['contentMd5']) && $requestConfig['data']['contentMd5']){
  386. $headers['Content-MD5'] = base64_encode(md5($result['body'],true));
  387. }
  388. }
  389. if($fileFlag && ($result['body'] instanceof StreamInterface)){
  390. if($this->methodName === 'uploadPart' && (isset($model['Offset']) || isset($model['PartSize']))){
  391. $bodySize = $result['body'] ->getSize();
  392. if(isset($model['Offset'])){
  393. $offset = intval($model['Offset']['value']);
  394. $offset = $offset >= 0 && $offset < $bodySize ? $offset : 0;
  395. }else{
  396. $offset = 0;
  397. }
  398. if(isset($model['PartSize'])){
  399. $partSize = intval($model['PartSize']['value']);
  400. $partSize = $partSize > 0 && $partSize <= ($bodySize - $offset) ? $partSize : $bodySize - $offset;
  401. }else{
  402. $partSize = $bodySize - $offset;
  403. }
  404. $result['body'] -> rewind();
  405. $result['body'] -> seek($offset);
  406. $headers['Content-Length'] = $partSize;
  407. }else if(isset($headers['Content-Length'])){
  408. $bodySize = $result['body'] -> getSize();
  409. if(intval($headers['Content-Length']) > $bodySize){
  410. $headers['Content-Length'] = $bodySize;
  411. }
  412. }
  413. }
  414. $constants = Constants::selectConstants($this -> signature);
  415. if($this->securityToken){
  416. $headers[$constants::SECURITY_TOKEN_HEAD] = $this->securityToken;
  417. }
  418. $headers['Host'] = $host;
  419. $result['host'] = $host;
  420. $result['method'] = $method;
  421. $result['headers'] = $headers;
  422. $result['pathArgs'] = $pathArgs;
  423. $result['dnsParam'] = $dnsParam;
  424. $result['uriParam'] = $uriParam;
  425. $result['requestUrl'] = $requestUrl;
  426. return $result;
  427. }
  428. }