PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/lslucas/105fm
PHP | 229 lines | 133 code | 25 blank | 71 comment | 14 complexity | 30b110028a85c0ce1bbd81f717bd3e6b 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 Aws\Common\Enum\DateFormat;
  19. use Guzzle\Http\Message\RequestInterface;
  20. use Guzzle\Http\QueryString;
  21. use Guzzle\Http\Url;
  22. /**
  23. * Default Amazon S3 signature implementation
  24. * @link http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
  25. */
  26. class S3Signature implements S3SignatureInterface
  27. {
  28. /**
  29. * @var array Query string values that must be signed
  30. */
  31. protected $signableQueryString = array (
  32. 'acl',
  33. 'cors',
  34. 'delete',
  35. 'lifecycle',
  36. 'location',
  37. 'logging',
  38. 'notification',
  39. 'partNumber',
  40. 'policy',
  41. 'requestPayment',
  42. 'response-cache-control',
  43. 'response-content-disposition',
  44. 'response-content-encoding',
  45. 'response-content-language',
  46. 'response-content-type',
  47. 'response-expires',
  48. 'restore',
  49. 'tagging',
  50. 'torrent',
  51. 'uploadId',
  52. 'uploads',
  53. 'versionId',
  54. 'versioning',
  55. 'versions',
  56. 'website',
  57. );
  58. /**
  59. * @var array Sorted headers that must be signed
  60. */
  61. protected $signableHeaders = array('Content-MD5', 'Content-Type');
  62. /**
  63. * {@inheritdoc}
  64. */
  65. public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
  66. {
  67. // Ensure that the signable query string parameters are sorted
  68. sort($this->signableQueryString);
  69. // Add the security token header if one is being used by the credentials
  70. if ($token = $credentials->getSecurityToken()) {
  71. $request->setHeader('x-amz-security-token', $token);
  72. }
  73. // Add a date header if one is not set
  74. if (!$request->hasHeader('date') && !$request->hasHeader('x-amz-date')) {
  75. $request->setHeader('Date', gmdate(DateFormat::RFC2822));
  76. }
  77. $stringToSign = $this->createCanonicalizedString($request);
  78. $request->getParams()->set('aws.string_to_sign', $stringToSign);
  79. $request->setHeader(
  80. 'Authorization',
  81. 'AWS ' . $credentials->getAccessKeyId() . ':' . $this->signString($stringToSign, $credentials)
  82. );
  83. }
  84. /**
  85. * {@inheritdoc}
  86. */
  87. public function signString($string, CredentialsInterface $credentials)
  88. {
  89. return base64_encode(hash_hmac('sha1', $string, $credentials->getSecretKey(), true));
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. public function createCanonicalizedString(RequestInterface $request, $expires = null)
  95. {
  96. $buffer = $request->getMethod() . "\n";
  97. // Add the interesting headers
  98. foreach ($this->signableHeaders as $header) {
  99. $buffer .= (string) $request->getHeader($header) . "\n";
  100. }
  101. // Choose dates from left to right based on what's set
  102. $date = $expires ?: (string) $request->getHeader('date');
  103. $buffer .= "{$date}\n"
  104. . $this->createCanonicalizedAmzHeaders($request)
  105. . $this->createCanonicalizedResource($request);
  106. return $buffer;
  107. }
  108. /**
  109. * Create a canonicalized AmzHeaders string for a signature.
  110. *
  111. * @param RequestInterface $request Request from which to gather headers
  112. *
  113. * @return string Returns canonicalized AMZ headers.
  114. */
  115. protected function createCanonicalizedAmzHeaders(RequestInterface $request)
  116. {
  117. $headers = array();
  118. foreach ($request->getHeaders(true) as $header) {
  119. /** @var $header \Guzzle\Http\Message\Header */
  120. $name = strtolower($header->getName());
  121. if (strpos($name, 'x-amz-') === 0) {
  122. $value = trim((string) $header);
  123. if ($value || $value === '0') {
  124. $headers[$name] = $name . ':' . $value;
  125. }
  126. }
  127. }
  128. if (empty($headers)) {
  129. return '';
  130. } else {
  131. ksort($headers);
  132. return implode("\n", $headers) . "\n";
  133. }
  134. }
  135. /**
  136. * Create a canonicalized resource for a request
  137. *
  138. * @param RequestInterface $request Request for the resource
  139. *
  140. * @return string
  141. */
  142. protected function createCanonicalizedResource(RequestInterface $request)
  143. {
  144. $buffer = $request->getParams()->get('s3.resource');
  145. // When sending a raw HTTP request (e.g. $client->get())
  146. if (null === $buffer) {
  147. $bucket = $request->getParams()->get('bucket') ?: $this->parseBucketName($request);
  148. // Use any specified bucket name, the parsed bucket name, or no bucket name when interacting with GetService
  149. $buffer = $bucket ? "/{$bucket}" : '';
  150. // Remove encoding from the path and use the S3 specific encoding
  151. $path = S3Client::encodeKey(rawurldecode($request->getPath()));
  152. // if the bucket was path style, then ensure that the bucket wasn't duplicated in the resource
  153. $buffer .= preg_replace("#^/{$bucket}/{$bucket}#", "/{$bucket}", $path);
  154. }
  155. // Remove double slashes
  156. $buffer = str_replace('//', '/', $buffer);
  157. // Add sub resource parameters
  158. $query = $request->getQuery();
  159. $first = true;
  160. foreach ($this->signableQueryString as $key) {
  161. if ($value = $query->get($key)) {
  162. $buffer .= $first ? '?' : '&';
  163. $first = false;
  164. $buffer .= $key;
  165. if ($value !== QueryString::BLANK) {
  166. $buffer .= "={$value}";
  167. }
  168. }
  169. }
  170. return $buffer;
  171. }
  172. /**
  173. * Parse the bucket name from a request object
  174. *
  175. * @param RequestInterface $request Request to parse
  176. *
  177. * @return string
  178. */
  179. protected function parseBucketName(RequestInterface $request)
  180. {
  181. $baseUrl = Url::factory($request->getClient()->getBaseUrl());
  182. $baseHost = $baseUrl->getHost();
  183. $host = $request->getHost();
  184. if (strpos($host, $baseHost) === false) {
  185. // Does not contain the base URL, so it's either a redirect, CNAME, or using a different region
  186. $baseHost = '';
  187. // For every known S3 host, check if that host is present on the request
  188. $regions = $request->getClient()->getDescription()->getData('regions');
  189. foreach ($regions as $region) {
  190. if (strpos($host, $region['hostname']) !== false) {
  191. // This host matches the request host. Tells use the region and endpoint-- we can derive the bucket
  192. $baseHost = $region['hostname'];
  193. break;
  194. }
  195. }
  196. // If no matching base URL was found, then assume that this is a CNAME, and the CNAME is the bucket
  197. if (!$baseHost) {
  198. return $host;
  199. }
  200. }
  201. // Remove the baseURL from the host of the request to attempt to determine the bucket name
  202. return trim(str_replace($baseHost, '', $request->getHost()), ' .');
  203. }
  204. }