PageRenderTime 25ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/api/vendor/aws/aws-sdk-php/src/Aws/S3/S3Signature.php

https://gitlab.com/x33n/respond
PHP | 268 lines | 169 code | 33 blank | 66 comment | 16 complexity | 7e3a6c582b4d5171040b348f575ed302 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License").
  6. * You may not use this file except in compliance with the License.
  7. * A copy of the License is located at
  8. *
  9. * http://aws.amazon.com/apache2.0
  10. *
  11. * or in the "license" file accompanying this file. This file is distributed
  12. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  13. * express or implied. See the License for the specific language governing
  14. * permissions and limitations under the License.
  15. */
  16. namespace Aws\S3;
  17. use Aws\Common\Credentials\CredentialsInterface;
  18. use Guzzle\Http\Message\RequestInterface;
  19. use Guzzle\Http\QueryString;
  20. use Guzzle\Http\Url;
  21. /**
  22. * Default Amazon S3 signature implementation
  23. * @link http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
  24. */
  25. class S3Signature implements S3SignatureInterface
  26. {
  27. /**
  28. * @var array Query string values that must be signed
  29. */
  30. protected $signableQueryString = array (
  31. 'acl',
  32. 'cors',
  33. 'delete',
  34. 'lifecycle',
  35. 'location',
  36. 'logging',
  37. 'notification',
  38. 'partNumber',
  39. 'policy',
  40. 'requestPayment',
  41. 'response-cache-control',
  42. 'response-content-disposition',
  43. 'response-content-encoding',
  44. 'response-content-language',
  45. 'response-content-type',
  46. 'response-expires',
  47. 'restore',
  48. 'tagging',
  49. 'torrent',
  50. 'uploadId',
  51. 'uploads',
  52. 'versionId',
  53. 'versioning',
  54. 'versions',
  55. 'website',
  56. );
  57. /** @var array Sorted headers that must be signed */
  58. private $signableHeaders = array('Content-MD5', 'Content-Type');
  59. public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
  60. {
  61. // Ensure that the signable query string parameters are sorted
  62. sort($this->signableQueryString);
  63. // Add the security token header if one is being used by the credentials
  64. if ($token = $credentials->getSecurityToken()) {
  65. $request->setHeader('x-amz-security-token', $token);
  66. }
  67. // Add a date header if one is not set
  68. if (!$request->hasHeader('date') && !$request->hasHeader('x-amz-date')) {
  69. $request->setHeader('Date', gmdate(\DateTime::RFC2822));
  70. }
  71. $stringToSign = $this->createCanonicalizedString($request);
  72. $request->getParams()->set('aws.string_to_sign', $stringToSign);
  73. $request->setHeader(
  74. 'Authorization',
  75. 'AWS ' . $credentials->getAccessKeyId() . ':' . $this->signString($stringToSign, $credentials)
  76. );
  77. }
  78. public function createPresignedUrl(
  79. RequestInterface $request,
  80. CredentialsInterface $credentials,
  81. $expires
  82. ) {
  83. if ($expires instanceof \DateTime) {
  84. $expires = $expires->getTimestamp();
  85. } elseif (!is_numeric($expires)) {
  86. $expires = strtotime($expires);
  87. }
  88. // Operate on a clone of the request, so the original is not altered
  89. $request = clone $request;
  90. // URL encoding already occurs in the URI template expansion. Undo that and encode using the same encoding as
  91. // GET object, PUT object, etc.
  92. $path = S3Client::encodeKey(rawurldecode($request->getPath()));
  93. $request->setPath($path);
  94. // Make sure to handle temporary credentials
  95. if ($token = $credentials->getSecurityToken()) {
  96. $request->setHeader('x-amz-security-token', $token);
  97. $request->getQuery()->set('x-amz-security-token', $token);
  98. }
  99. // Set query params required for pre-signed URLs
  100. $request->getQuery()
  101. ->set('AWSAccessKeyId', $credentials->getAccessKeyId())
  102. ->set('Expires', $expires)
  103. ->set('Signature', $this->signString(
  104. $this->createCanonicalizedString($request, $expires),
  105. $credentials
  106. ));
  107. // Move X-Amz-* headers to the query string
  108. foreach ($request->getHeaders() as $name => $header) {
  109. $name = strtolower($name);
  110. if (strpos($name, 'x-amz-') === 0) {
  111. $request->getQuery()->set($name, (string) $header);
  112. $request->removeHeader($name);
  113. }
  114. }
  115. return $request->getUrl();
  116. }
  117. public function signString($string, CredentialsInterface $credentials)
  118. {
  119. return base64_encode(hash_hmac('sha1', $string, $credentials->getSecretKey(), true));
  120. }
  121. public function createCanonicalizedString(RequestInterface $request, $expires = null)
  122. {
  123. $buffer = $request->getMethod() . "\n";
  124. // Add the interesting headers
  125. foreach ($this->signableHeaders as $header) {
  126. $buffer .= (string) $request->getHeader($header) . "\n";
  127. }
  128. // Choose dates from left to right based on what's set
  129. $date = $expires ?: (string) $request->getHeader('date');
  130. $buffer .= "{$date}\n"
  131. . $this->createCanonicalizedAmzHeaders($request)
  132. . $this->createCanonicalizedResource($request);
  133. return $buffer;
  134. }
  135. /**
  136. * Create a canonicalized AmzHeaders string for a signature.
  137. *
  138. * @param RequestInterface $request Request from which to gather headers
  139. *
  140. * @return string Returns canonicalized AMZ headers.
  141. */
  142. private function createCanonicalizedAmzHeaders(RequestInterface $request)
  143. {
  144. $headers = array();
  145. foreach ($request->getHeaders() as $name => $header) {
  146. $name = strtolower($name);
  147. if (strpos($name, 'x-amz-') === 0) {
  148. $value = trim((string) $header);
  149. if ($value || $value === '0') {
  150. $headers[$name] = $name . ':' . $value;
  151. }
  152. }
  153. }
  154. if (!$headers) {
  155. return '';
  156. }
  157. ksort($headers);
  158. return implode("\n", $headers) . "\n";
  159. }
  160. /**
  161. * Create a canonicalized resource for a request
  162. *
  163. * @param RequestInterface $request Request for the resource
  164. *
  165. * @return string
  166. */
  167. private function createCanonicalizedResource(RequestInterface $request)
  168. {
  169. $buffer = $request->getParams()->get('s3.resource');
  170. // When sending a raw HTTP request (e.g. $client->get())
  171. if (null === $buffer) {
  172. $bucket = $request->getParams()->get('bucket') ?: $this->parseBucketName($request);
  173. // Use any specified bucket name, the parsed bucket name, or no bucket name when interacting with GetService
  174. $buffer = $bucket ? "/{$bucket}" : '';
  175. // Remove encoding from the path and use the S3 specific encoding
  176. $path = S3Client::encodeKey(rawurldecode($request->getPath()));
  177. // if the bucket was path style, then ensure that the bucket wasn't duplicated in the resource
  178. $buffer .= preg_replace("#^/{$bucket}/{$bucket}#", "/{$bucket}", $path);
  179. }
  180. // Remove double slashes
  181. $buffer = str_replace('//', '/', $buffer);
  182. // Add sub resource parameters
  183. $query = $request->getQuery();
  184. $first = true;
  185. foreach ($this->signableQueryString as $key) {
  186. if ($query->hasKey($key)) {
  187. $value = $query[$key];
  188. $buffer .= $first ? '?' : '&';
  189. $first = false;
  190. $buffer .= $key;
  191. // Don't add values for empty sub-resources
  192. if ($value !== '' &&
  193. $value !== false &&
  194. $value !== null &&
  195. $value !== QueryString::BLANK
  196. ) {
  197. $buffer .= "={$value}";
  198. }
  199. }
  200. }
  201. return $buffer;
  202. }
  203. /**
  204. * Parse the bucket name from a request object
  205. *
  206. * @param RequestInterface $request Request to parse
  207. *
  208. * @return string
  209. */
  210. private function parseBucketName(RequestInterface $request)
  211. {
  212. $baseUrl = Url::factory($request->getClient()->getBaseUrl());
  213. $baseHost = $baseUrl->getHost();
  214. $host = $request->getHost();
  215. if (strpos($host, $baseHost) === false) {
  216. // Does not contain the base URL, so it's either a redirect, CNAME, or using a different region
  217. $baseHost = '';
  218. // For every known S3 host, check if that host is present on the request
  219. $regions = $request->getClient()->getDescription()->getData('regions');
  220. foreach ($regions as $region) {
  221. if (strpos($host, $region['hostname']) !== false) {
  222. // This host matches the request host. Tells use the region and endpoint-- we can derive the bucket
  223. $baseHost = $region['hostname'];
  224. break;
  225. }
  226. }
  227. // If no matching base URL was found, then assume that this is a CNAME, and the CNAME is the bucket
  228. if (!$baseHost) {
  229. return $host;
  230. }
  231. }
  232. // Remove the baseURL from the host of the request to attempt to determine the bucket name
  233. return trim(str_replace($baseHost, '', $request->getHost()), ' .');
  234. }
  235. }