PresignUrlMiddleware.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <?php
  2. namespace Aws;
  3. use Aws\Signature\SignatureV4;
  4. use Aws\Endpoint\EndpointProvider;
  5. use GuzzleHttp\Psr7\Uri;
  6. use Psr\Http\Message\RequestInterface;
  7. /**
  8. * @internal Adds computed values to service operations that need presigned url.
  9. */
  10. class PresignUrlMiddleware
  11. {
  12. private $client;
  13. private $endpointProvider;
  14. private $nextHandler;
  15. /** @var array names of operations that require presign url */
  16. private $commandPool;
  17. /** @var array query params that are not on the operation's model to add before signing */
  18. private $extraQueryParams;
  19. /** @var string */
  20. private $serviceName;
  21. /** @var string */
  22. private $presignParam;
  23. /** @var bool */
  24. private $requireDifferentRegion;
  25. public function __construct(
  26. array $options,
  27. $endpointProvider,
  28. AwsClientInterface $client,
  29. callable $nextHandler
  30. ) {
  31. $this->endpointProvider = $endpointProvider;
  32. $this->client = $client;
  33. $this->nextHandler = $nextHandler;
  34. $this->commandPool = $options['operations'];
  35. $this->serviceName = $options['service'];
  36. $this->presignParam = !empty($options['presign_param'])
  37. ? $options['presign_param']
  38. : 'PresignedUrl';
  39. $this->extraQueryParams = !empty($options['extra_query_params'])
  40. ? $options['extra_query_params']
  41. : [];
  42. $this->requireDifferentRegion = !empty($options['require_different_region']);
  43. }
  44. public static function wrap(
  45. AwsClientInterface $client,
  46. $endpointProvider,
  47. array $options = []
  48. ) {
  49. return function (callable $handler) use ($endpointProvider, $client, $options) {
  50. $f = new PresignUrlMiddleware($options, $endpointProvider, $client, $handler);
  51. return $f;
  52. };
  53. }
  54. public function __invoke(CommandInterface $cmd, RequestInterface $request = null)
  55. {
  56. if (in_array($cmd->getName(), $this->commandPool)
  57. && (!isset($cmd->{'__skip' . $cmd->getName()}))
  58. ) {
  59. $cmd['DestinationRegion'] = $this->client->getRegion();
  60. if (!empty($cmd['SourceRegion']) && !empty($cmd[$this->presignParam])) {
  61. goto nexthandler;
  62. }
  63. if (!$this->requireDifferentRegion
  64. || (!empty($cmd['SourceRegion'])
  65. && $cmd['SourceRegion'] !== $cmd['DestinationRegion'])
  66. ) {
  67. $cmd[$this->presignParam] = $this->createPresignedUrl($this->client, $cmd);
  68. }
  69. }
  70. nexthandler:
  71. $nextHandler = $this->nextHandler;
  72. return $nextHandler($cmd, $request);
  73. }
  74. private function createPresignedUrl(
  75. AwsClientInterface $client,
  76. CommandInterface $cmd
  77. ) {
  78. $cmdName = $cmd->getName();
  79. $newCmd = $client->getCommand($cmdName, $cmd->toArray());
  80. // Avoid infinite recursion by flagging the new command.
  81. $newCmd->{'__skip' . $cmdName} = true;
  82. // Serialize a request for the operation.
  83. $request = \Aws\serialize($newCmd);
  84. // Create the new endpoint for the target endpoint.
  85. if ($this->endpointProvider instanceof \Aws\EndpointV2\EndpointProviderV2) {
  86. $providerArgs = array_merge(
  87. $this->client->getEndpointProviderArgs(),
  88. ['Region' => $cmd['SourceRegion']]
  89. );
  90. $endpoint = $this->endpointProvider->resolveEndpoint($providerArgs)->getUrl();
  91. } else {
  92. $endpoint = EndpointProvider::resolve($this->endpointProvider, [
  93. 'region' => $cmd['SourceRegion'],
  94. 'service' => $this->serviceName,
  95. ])['endpoint'];
  96. }
  97. // Set the request to hit the target endpoint.
  98. $uri = $request->getUri()->withHost((new Uri($endpoint))->getHost());
  99. $request = $request->withUri($uri);
  100. // Create a presigned URL for our generated request.
  101. $signer = new SignatureV4($this->serviceName, $cmd['SourceRegion']);
  102. $currentQueryParams = (string) $request->getBody();
  103. $paramsToAdd = false;
  104. if (!empty($this->extraQueryParams[$cmdName])) {
  105. foreach ($this->extraQueryParams[$cmdName] as $param) {
  106. if (!strpos($currentQueryParams, $param)) {
  107. $paramsToAdd = "&{$param}={$cmd[$param]}";
  108. }
  109. }
  110. }
  111. return (string) $signer->presign(
  112. SignatureV4::convertPostToGet($request, $paramsToAdd ?: ""),
  113. $client->getCredentials()->wait(),
  114. '+1 hour'
  115. )->getUri();
  116. }
  117. }