AnonymousSignature.php
11 months ago
S3ExpressSignature.php
11 months ago
S3SignatureV4.php
11 months ago
SignatureInterface.php
11 months ago
SignatureProvider.php
11 months ago
SignatureTrait.php
11 months ago
SignatureV4.php
11 months ago
SignatureV4.php
577 lines
| 1 | <?php |
| 2 | namespace Aws\Signature; |
| 3 | |
| 4 | use Aws\Credentials\CredentialsInterface; |
| 5 | use AWS\CRT\Auth\Signable; |
| 6 | use AWS\CRT\Auth\SignatureType; |
| 7 | use AWS\CRT\Auth\Signing; |
| 8 | use AWS\CRT\Auth\SigningAlgorithm; |
| 9 | use AWS\CRT\Auth\SigningConfigAWS; |
| 10 | use AWS\CRT\Auth\StaticCredentialsProvider; |
| 11 | use AWS\CRT\HTTP\Request; |
| 12 | use Aws\Exception\CommonRuntimeException; |
| 13 | use Aws\Exception\CouldNotCreateChecksumException; |
| 14 | use GuzzleHttp\Psr7; |
| 15 | use Psr\Http\Message\RequestInterface; |
| 16 | |
| 17 | /** |
| 18 | * Signature Version 4 |
| 19 | * @link http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html |
| 20 | */ |
| 21 | class SignatureV4 implements SignatureInterface |
| 22 | { |
| 23 | use SignatureTrait; |
| 24 | const ISO8601_BASIC = 'Ymd\THis\Z'; |
| 25 | const UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD'; |
| 26 | const AMZ_CONTENT_SHA256_HEADER = 'X-Amz-Content-Sha256'; |
| 27 | |
| 28 | /** @var string */ |
| 29 | private $service; |
| 30 | |
| 31 | /** @var string */ |
| 32 | protected $region; |
| 33 | |
| 34 | /** @var bool */ |
| 35 | private $unsigned; |
| 36 | |
| 37 | /** @var bool */ |
| 38 | private $useV4a; |
| 39 | |
| 40 | /** |
| 41 | * The following headers are not signed because signing these headers |
| 42 | * would potentially cause a signature mismatch when sending a request |
| 43 | * through a proxy or if modified at the HTTP client level. |
| 44 | * |
| 45 | * @return array |
| 46 | */ |
| 47 | protected function getHeaderBlacklist() |
| 48 | { |
| 49 | return [ |
| 50 | 'cache-control' => true, |
| 51 | 'content-type' => true, |
| 52 | 'content-length' => true, |
| 53 | 'expect' => true, |
| 54 | 'max-forwards' => true, |
| 55 | 'pragma' => true, |
| 56 | 'range' => true, |
| 57 | 'te' => true, |
| 58 | 'if-match' => true, |
| 59 | 'if-none-match' => true, |
| 60 | 'if-modified-since' => true, |
| 61 | 'if-unmodified-since' => true, |
| 62 | 'if-range' => true, |
| 63 | 'accept' => true, |
| 64 | 'authorization' => true, |
| 65 | 'proxy-authorization' => true, |
| 66 | 'from' => true, |
| 67 | 'referer' => true, |
| 68 | 'user-agent' => true, |
| 69 | 'X-Amz-User-Agent' => true, |
| 70 | 'x-amzn-trace-id' => true, |
| 71 | 'aws-sdk-invocation-id' => true, |
| 72 | 'aws-sdk-retry' => true, |
| 73 | ]; |
| 74 | } |
| 75 | |
| 76 | /** |
| 77 | * @param string $service Service name to use when signing |
| 78 | * @param string $region Region name to use when signing |
| 79 | * @param array $options Array of configuration options used when signing |
| 80 | * - unsigned-body: Flag to make request have unsigned payload. |
| 81 | * Unsigned body is used primarily for streaming requests. |
| 82 | */ |
| 83 | public function __construct($service, $region, array $options = []) |
| 84 | { |
| 85 | $this->service = $service; |
| 86 | $this->region = $region; |
| 87 | $this->unsigned = isset($options['unsigned-body']) ? $options['unsigned-body'] : false; |
| 88 | $this->useV4a = isset($options['use_v4a']) && $options['use_v4a'] === true; |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * {@inheritdoc} |
| 93 | */ |
| 94 | public function signRequest( |
| 95 | RequestInterface $request, |
| 96 | CredentialsInterface $credentials, |
| 97 | $signingService = null |
| 98 | ) { |
| 99 | $ldt = gmdate(self::ISO8601_BASIC); |
| 100 | $sdt = substr($ldt, 0, 8); |
| 101 | $parsed = $this->parseRequest($request); |
| 102 | $parsed['headers']['X-Amz-Date'] = [$ldt]; |
| 103 | |
| 104 | if ($token = $credentials->getSecurityToken()) { |
| 105 | $parsed['headers']['X-Amz-Security-Token'] = [$token]; |
| 106 | } |
| 107 | $service = isset($signingService) ? $signingService : $this->service; |
| 108 | |
| 109 | if ($this->useV4a) { |
| 110 | return $this->signWithV4a($credentials, $request, $service); |
| 111 | } |
| 112 | |
| 113 | $cs = $this->createScope($sdt, $this->region, $service); |
| 114 | $payload = $this->getPayload($request); |
| 115 | |
| 116 | if ($payload == self::UNSIGNED_PAYLOAD) { |
| 117 | $parsed['headers'][self::AMZ_CONTENT_SHA256_HEADER] = [$payload]; |
| 118 | } |
| 119 | |
| 120 | $context = $this->createContext($parsed, $payload); |
| 121 | $toSign = $this->createStringToSign($ldt, $cs, $context['creq']); |
| 122 | $signingKey = $this->getSigningKey( |
| 123 | $sdt, |
| 124 | $this->region, |
| 125 | $service, |
| 126 | $credentials->getSecretKey() |
| 127 | ); |
| 128 | $signature = hash_hmac('sha256', $toSign, $signingKey); |
| 129 | $parsed['headers']['Authorization'] = [ |
| 130 | "AWS4-HMAC-SHA256 " |
| 131 | . "Credential={$credentials->getAccessKeyId()}/{$cs}, " |
| 132 | . "SignedHeaders={$context['headers']}, Signature={$signature}" |
| 133 | ]; |
| 134 | |
| 135 | return $this->buildRequest($parsed); |
| 136 | } |
| 137 | |
| 138 | /** |
| 139 | * Get the headers that were used to pre-sign the request. |
| 140 | * Used for the X-Amz-SignedHeaders header. |
| 141 | * |
| 142 | * @param array $headers |
| 143 | * @return array |
| 144 | */ |
| 145 | private function getPresignHeaders(array $headers) |
| 146 | { |
| 147 | $presignHeaders = []; |
| 148 | $blacklist = $this->getHeaderBlacklist(); |
| 149 | foreach ($headers as $name => $value) { |
| 150 | $lName = strtolower($name); |
| 151 | if (!isset($blacklist[$lName]) |
| 152 | && $name !== self::AMZ_CONTENT_SHA256_HEADER |
| 153 | ) { |
| 154 | $presignHeaders[] = $lName; |
| 155 | } |
| 156 | } |
| 157 | return $presignHeaders; |
| 158 | } |
| 159 | |
| 160 | /** |
| 161 | * {@inheritdoc} |
| 162 | */ |
| 163 | public function presign( |
| 164 | RequestInterface $request, |
| 165 | CredentialsInterface $credentials, |
| 166 | $expires, |
| 167 | array $options = [] |
| 168 | ) { |
| 169 | $startTimestamp = isset($options['start_time']) |
| 170 | ? $this->convertToTimestamp($options['start_time'], null) |
| 171 | : time(); |
| 172 | $expiresTimestamp = $this->convertToTimestamp($expires, $startTimestamp); |
| 173 | |
| 174 | if ($this->useV4a) { |
| 175 | return $this->presignWithV4a( |
| 176 | $request, |
| 177 | $credentials, |
| 178 | $this->convertExpires($expiresTimestamp, $startTimestamp) |
| 179 | ); |
| 180 | } |
| 181 | |
| 182 | $parsed = $this->createPresignedRequest($request, $credentials); |
| 183 | |
| 184 | $payload = $this->getPresignedPayload($request); |
| 185 | $httpDate = gmdate(self::ISO8601_BASIC, $startTimestamp); |
| 186 | $shortDate = substr($httpDate, 0, 8); |
| 187 | $scope = $this->createScope($shortDate, $this->region, $this->service); |
| 188 | $credential = $credentials->getAccessKeyId() . '/' . $scope; |
| 189 | if ($credentials->getSecurityToken()) { |
| 190 | unset($parsed['headers']['X-Amz-Security-Token']); |
| 191 | } |
| 192 | $parsed['query']['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256'; |
| 193 | $parsed['query']['X-Amz-Credential'] = $credential; |
| 194 | $parsed['query']['X-Amz-Date'] = gmdate('Ymd\THis\Z', $startTimestamp); |
| 195 | $parsed['query']['X-Amz-SignedHeaders'] = implode(';', $this->getPresignHeaders($parsed['headers'])); |
| 196 | $parsed['query']['X-Amz-Expires'] = $this->convertExpires($expiresTimestamp, $startTimestamp); |
| 197 | $context = $this->createContext($parsed, $payload); |
| 198 | $stringToSign = $this->createStringToSign($httpDate, $scope, $context['creq']); |
| 199 | $key = $this->getSigningKey( |
| 200 | $shortDate, |
| 201 | $this->region, |
| 202 | $this->service, |
| 203 | $credentials->getSecretKey() |
| 204 | ); |
| 205 | $parsed['query']['X-Amz-Signature'] = hash_hmac('sha256', $stringToSign, $key); |
| 206 | |
| 207 | return $this->buildRequest($parsed); |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Converts a POST request to a GET request by moving POST fields into the |
| 212 | * query string. |
| 213 | * |
| 214 | * Useful for pre-signing query protocol requests. |
| 215 | * |
| 216 | * @param RequestInterface $request Request to clone |
| 217 | * |
| 218 | * @return RequestInterface |
| 219 | * @throws \InvalidArgumentException if the method is not POST |
| 220 | */ |
| 221 | public static function convertPostToGet(RequestInterface $request, $additionalQueryParams = "") |
| 222 | { |
| 223 | if ($request->getMethod() !== 'POST') { |
| 224 | throw new \InvalidArgumentException('Expected a POST request but ' |
| 225 | . 'received a ' . $request->getMethod() . ' request.'); |
| 226 | } |
| 227 | |
| 228 | $sr = $request->withMethod('GET') |
| 229 | ->withBody(Psr7\Utils::streamFor('')) |
| 230 | ->withoutHeader('Content-Type') |
| 231 | ->withoutHeader('Content-Length'); |
| 232 | |
| 233 | // Move POST fields to the query if they are present |
| 234 | if ($request->getHeaderLine('Content-Type') === 'application/x-www-form-urlencoded') { |
| 235 | $body = (string) $request->getBody() . $additionalQueryParams; |
| 236 | $sr = $sr->withUri($sr->getUri()->withQuery($body)); |
| 237 | } |
| 238 | |
| 239 | return $sr; |
| 240 | } |
| 241 | |
| 242 | protected function getPayload(RequestInterface $request) |
| 243 | { |
| 244 | if ($this->unsigned && $request->getUri()->getScheme() == 'https') { |
| 245 | return self::UNSIGNED_PAYLOAD; |
| 246 | } |
| 247 | // Calculate the request signature payload |
| 248 | if ($request->hasHeader(self::AMZ_CONTENT_SHA256_HEADER)) { |
| 249 | // Handle streaming operations (e.g. Glacier.UploadArchive) |
| 250 | return $request->getHeaderLine(self::AMZ_CONTENT_SHA256_HEADER); |
| 251 | } |
| 252 | |
| 253 | if (!$request->getBody()->isSeekable()) { |
| 254 | throw new CouldNotCreateChecksumException('sha256'); |
| 255 | } |
| 256 | |
| 257 | try { |
| 258 | return Psr7\Utils::hash($request->getBody(), 'sha256'); |
| 259 | } catch (\Exception $e) { |
| 260 | throw new CouldNotCreateChecksumException('sha256', $e); |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | protected function getPresignedPayload(RequestInterface $request) |
| 265 | { |
| 266 | return $this->getPayload($request); |
| 267 | } |
| 268 | |
| 269 | protected function createCanonicalizedPath($path) |
| 270 | { |
| 271 | $doubleEncoded = rawurlencode(ltrim($path, '/')); |
| 272 | |
| 273 | return '/' . str_replace('%2F', '/', $doubleEncoded); |
| 274 | } |
| 275 | |
| 276 | private function createStringToSign($longDate, $credentialScope, $creq) |
| 277 | { |
| 278 | $hash = hash('sha256', $creq); |
| 279 | |
| 280 | return "AWS4-HMAC-SHA256\n{$longDate}\n{$credentialScope}\n{$hash}"; |
| 281 | } |
| 282 | |
| 283 | private function createPresignedRequest( |
| 284 | RequestInterface $request, |
| 285 | CredentialsInterface $credentials |
| 286 | ) { |
| 287 | $parsedRequest = $this->parseRequest($request); |
| 288 | |
| 289 | // Make sure to handle temporary credentials |
| 290 | if ($token = $credentials->getSecurityToken()) { |
| 291 | $parsedRequest['headers']['X-Amz-Security-Token'] = [$token]; |
| 292 | } |
| 293 | |
| 294 | return $this->moveHeadersToQuery($parsedRequest); |
| 295 | } |
| 296 | |
| 297 | /** |
| 298 | * @param array $parsedRequest |
| 299 | * @param string $payload Hash of the request payload |
| 300 | * @return array Returns an array of context information |
| 301 | */ |
| 302 | private function createContext(array $parsedRequest, $payload) |
| 303 | { |
| 304 | $blacklist = $this->getHeaderBlacklist(); |
| 305 | |
| 306 | // Normalize the path as required by SigV4 |
| 307 | $canon = $parsedRequest['method'] . "\n" |
| 308 | . $this->createCanonicalizedPath($parsedRequest['path']) . "\n" |
| 309 | . $this->getCanonicalizedQuery($parsedRequest['query']) . "\n"; |
| 310 | |
| 311 | // Case-insensitively aggregate all of the headers. |
| 312 | $aggregate = []; |
| 313 | foreach ($parsedRequest['headers'] as $key => $values) { |
| 314 | $key = strtolower($key); |
| 315 | if (!isset($blacklist[$key])) { |
| 316 | foreach ($values as $v) { |
| 317 | $aggregate[$key][] = $v; |
| 318 | } |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | ksort($aggregate); |
| 323 | $canonHeaders = []; |
| 324 | foreach ($aggregate as $k => $v) { |
| 325 | if (count($v) > 0) { |
| 326 | sort($v); |
| 327 | } |
| 328 | $canonHeaders[] = $k . ':' . preg_replace('/\s+/', ' ', implode(',', $v)); |
| 329 | } |
| 330 | |
| 331 | $signedHeadersString = implode(';', array_keys($aggregate)); |
| 332 | $canon .= implode("\n", $canonHeaders) . "\n\n" |
| 333 | . $signedHeadersString . "\n" |
| 334 | . $payload; |
| 335 | |
| 336 | return ['creq' => $canon, 'headers' => $signedHeadersString]; |
| 337 | } |
| 338 | |
| 339 | private function getCanonicalizedQuery(array $query) |
| 340 | { |
| 341 | unset($query['X-Amz-Signature']); |
| 342 | |
| 343 | if (!$query) { |
| 344 | return ''; |
| 345 | } |
| 346 | |
| 347 | $qs = ''; |
| 348 | ksort($query); |
| 349 | foreach ($query as $k => $v) { |
| 350 | if (!is_array($v)) { |
| 351 | $qs .= rawurlencode($k) . '=' . rawurlencode($v !== null ? $v : '') . '&'; |
| 352 | } else { |
| 353 | sort($v); |
| 354 | foreach ($v as $value) { |
| 355 | $qs .= rawurlencode($k) . '=' . rawurlencode($value !== null ? $value : '') . '&'; |
| 356 | } |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | return substr($qs, 0, -1); |
| 361 | } |
| 362 | |
| 363 | private function convertToTimestamp($dateValue, $relativeTimeBase = null) |
| 364 | { |
| 365 | if ($dateValue instanceof \DateTimeInterface) { |
| 366 | $timestamp = $dateValue->getTimestamp(); |
| 367 | } elseif (!is_numeric($dateValue)) { |
| 368 | $timestamp = strtotime($dateValue, |
| 369 | $relativeTimeBase === null ? time() : $relativeTimeBase |
| 370 | ); |
| 371 | } else { |
| 372 | $timestamp = $dateValue; |
| 373 | } |
| 374 | |
| 375 | return $timestamp; |
| 376 | } |
| 377 | |
| 378 | private function convertExpires($expiresTimestamp, $startTimestamp) |
| 379 | { |
| 380 | $duration = $expiresTimestamp - $startTimestamp; |
| 381 | |
| 382 | // Ensure that the duration of the signature is not longer than a week |
| 383 | if ($duration > 604800) { |
| 384 | throw new \InvalidArgumentException('The expiration date of a ' |
| 385 | . 'signature version 4 presigned URL must be less than one ' |
| 386 | . 'week'); |
| 387 | } |
| 388 | |
| 389 | return $duration; |
| 390 | } |
| 391 | |
| 392 | private function moveHeadersToQuery(array $parsedRequest) |
| 393 | { |
| 394 | //x-amz-user-agent shouldn't be put in a query param |
| 395 | unset($parsedRequest['headers']['X-Amz-User-Agent']); |
| 396 | |
| 397 | foreach ($parsedRequest['headers'] as $name => $header) { |
| 398 | $lname = strtolower($name); |
| 399 | if (substr($lname, 0, 5) == 'x-amz') { |
| 400 | $parsedRequest['query'][$name] = $header; |
| 401 | } |
| 402 | $blacklist = $this->getHeaderBlacklist(); |
| 403 | if (isset($blacklist[$lname]) |
| 404 | || $lname === strtolower(self::AMZ_CONTENT_SHA256_HEADER) |
| 405 | ) { |
| 406 | unset($parsedRequest['headers'][$name]); |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | return $parsedRequest; |
| 411 | } |
| 412 | |
| 413 | private function parseRequest(RequestInterface $request) |
| 414 | { |
| 415 | // Clean up any previously set headers. |
| 416 | /** @var RequestInterface $request */ |
| 417 | $request = $request |
| 418 | ->withoutHeader('X-Amz-Date') |
| 419 | ->withoutHeader('Date') |
| 420 | ->withoutHeader('Authorization'); |
| 421 | $uri = $request->getUri(); |
| 422 | |
| 423 | return [ |
| 424 | 'method' => $request->getMethod(), |
| 425 | 'path' => $uri->getPath(), |
| 426 | 'query' => Psr7\Query::parse($uri->getQuery()), |
| 427 | 'uri' => $uri, |
| 428 | 'headers' => $request->getHeaders(), |
| 429 | 'body' => $request->getBody(), |
| 430 | 'version' => $request->getProtocolVersion() |
| 431 | ]; |
| 432 | } |
| 433 | |
| 434 | private function buildRequest(array $req) |
| 435 | { |
| 436 | if ($req['query']) { |
| 437 | $req['uri'] = $req['uri']->withQuery(Psr7\Query::build($req['query'])); |
| 438 | } |
| 439 | |
| 440 | return new Psr7\Request( |
| 441 | $req['method'], |
| 442 | $req['uri'], |
| 443 | $req['headers'], |
| 444 | $req['body'], |
| 445 | $req['version'] |
| 446 | ); |
| 447 | } |
| 448 | |
| 449 | private function verifyCRTLoaded() |
| 450 | { |
| 451 | if (!extension_loaded('awscrt')) { |
| 452 | throw new CommonRuntimeException( |
| 453 | "AWS Common Runtime for PHP is required to use Signature V4A" |
| 454 | . ". Please install it using the instructions found at" |
| 455 | . " https://github.com/aws/aws-sdk-php/blob/master/CRT_INSTRUCTIONS.md" |
| 456 | ); |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | private function createCRTStaticCredentialsProvider($credentials) |
| 461 | { |
| 462 | return new StaticCredentialsProvider([ |
| 463 | 'access_key_id' => $credentials->getAccessKeyId(), |
| 464 | 'secret_access_key' => $credentials->getSecretKey(), |
| 465 | 'session_token' => $credentials->getSecurityToken(), |
| 466 | ]); |
| 467 | } |
| 468 | |
| 469 | private function removeIllegalV4aHeaders(&$request) |
| 470 | { |
| 471 | $illegalV4aHeaders = [ |
| 472 | self::AMZ_CONTENT_SHA256_HEADER, |
| 473 | "aws-sdk-invocation-id", |
| 474 | "aws-sdk-retry", |
| 475 | 'x-amz-region-set' |
| 476 | ]; |
| 477 | $storedHeaders = []; |
| 478 | |
| 479 | foreach ($illegalV4aHeaders as $header) { |
| 480 | if ($request->hasHeader($header)){ |
| 481 | $storedHeaders[$header] = $request->getHeader($header); |
| 482 | $request = $request->withoutHeader($header); |
| 483 | } |
| 484 | } |
| 485 | |
| 486 | return $storedHeaders; |
| 487 | } |
| 488 | |
| 489 | private function CRTRequestFromGuzzleRequest($request) |
| 490 | { |
| 491 | return new Request( |
| 492 | $request->getMethod(), |
| 493 | (string) $request->getUri(), |
| 494 | [], //leave empty as the query is parsed from the uri object |
| 495 | array_map(function ($header) {return $header[0];}, $request->getHeaders()) |
| 496 | ); |
| 497 | } |
| 498 | |
| 499 | /** |
| 500 | * @param CredentialsInterface $credentials |
| 501 | * @param RequestInterface $request |
| 502 | * @param $signingService |
| 503 | * @return RequestInterface |
| 504 | */ |
| 505 | protected function signWithV4a(CredentialsInterface $credentials, RequestInterface $request, $signingService) |
| 506 | { |
| 507 | $this->verifyCRTLoaded(); |
| 508 | $credentials_provider = $this->createCRTStaticCredentialsProvider($credentials); |
| 509 | $signingConfig = new SigningConfigAWS([ |
| 510 | 'algorithm' => SigningAlgorithm::SIGv4_ASYMMETRIC, |
| 511 | 'signature_type' => SignatureType::HTTP_REQUEST_HEADERS, |
| 512 | 'credentials_provider' => $credentials_provider, |
| 513 | 'signed_body_value' => $this->getPayload($request), |
| 514 | 'region' => "*", |
| 515 | 'service' => $signingService, |
| 516 | 'date' => time(), |
| 517 | ]); |
| 518 | |
| 519 | $removedIllegalHeaders = $this->removeIllegalV4aHeaders($request); |
| 520 | $http_request = $this->CRTRequestFromGuzzleRequest($request); |
| 521 | |
| 522 | Signing::signRequestAws( |
| 523 | Signable::fromHttpRequest($http_request), |
| 524 | $signingConfig, function ($signing_result, $error_code) use (&$http_request) { |
| 525 | $signing_result->applyToHttpRequest($http_request); |
| 526 | }); |
| 527 | foreach ($removedIllegalHeaders as $header => $value) { |
| 528 | $request = $request->withHeader($header, $value); |
| 529 | } |
| 530 | |
| 531 | $sigV4AHeaders = $http_request->headers(); |
| 532 | foreach ($sigV4AHeaders->toArray() as $h => $v) { |
| 533 | $request = $request->withHeader($h, $v); |
| 534 | } |
| 535 | |
| 536 | return $request; |
| 537 | } |
| 538 | |
| 539 | protected function presignWithV4a( |
| 540 | RequestInterface $request, |
| 541 | CredentialsInterface $credentials, |
| 542 | $expires |
| 543 | ) |
| 544 | { |
| 545 | $this->verifyCRTLoaded(); |
| 546 | $credentials_provider = $this->createCRTStaticCredentialsProvider($credentials); |
| 547 | $signingConfig = new SigningConfigAWS([ |
| 548 | 'algorithm' => SigningAlgorithm::SIGv4_ASYMMETRIC, |
| 549 | 'signature_type' => SignatureType::HTTP_REQUEST_QUERY_PARAMS, |
| 550 | 'credentials_provider' => $credentials_provider, |
| 551 | 'signed_body_value' => $this->getPresignedPayload($request), |
| 552 | 'region' => "*", |
| 553 | 'service' => $this->service, |
| 554 | 'date' => time(), |
| 555 | 'expiration_in_seconds' => $expires |
| 556 | ]); |
| 557 | |
| 558 | $this->removeIllegalV4aHeaders($request); |
| 559 | foreach ($this->getHeaderBlacklist() as $headerName => $headerValue) { |
| 560 | if ($request->hasHeader($headerName)) { |
| 561 | $request = $request->withoutHeader($headerName); |
| 562 | } |
| 563 | } |
| 564 | |
| 565 | $http_request = $this->CRTRequestFromGuzzleRequest($request); |
| 566 | Signing::signRequestAws( |
| 567 | Signable::fromHttpRequest($http_request), |
| 568 | $signingConfig, function ($signing_result, $error_code) use (&$http_request) { |
| 569 | $signing_result->applyToHttpRequest($http_request); |
| 570 | }); |
| 571 | |
| 572 | return $request->withUri( |
| 573 | new Psr7\Uri($http_request->pathAndQuery()) |
| 574 | ); |
| 575 | } |
| 576 | } |
| 577 |