PluginProbe
Media Cloud Sync / 1.4.0
Media Cloud Sync v1.4.0
1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 All 35 releases
media-cloud-sync / includes / sdk / s3 / Aws / S3 / BucketEndpointArnMiddleware.php

BucketEndpointArnMiddleware.php in Media Cloud Sync 1.4.0, at includes/sdk/s3/Aws/S3/BucketEndpointArnMiddleware.php

242 lines 12.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Dudlewebs\WPMCS\s3\Aws\S3;
4
5 use Dudlewebs\WPMCS\s3\Aws\Api\Service;
6 use Dudlewebs\WPMCS\s3\Aws\Arn\AccessPointArnInterface;
7 use Dudlewebs\WPMCS\s3\Aws\Arn\ArnParser;
8 use Dudlewebs\WPMCS\s3\Aws\Arn\ObjectLambdaAccessPointArn;
9 use Dudlewebs\WPMCS\s3\Aws\Arn\Exception\InvalidArnException;
10 use Dudlewebs\WPMCS\s3\Aws\Arn\AccessPointArn as BaseAccessPointArn;
11 use Dudlewebs\WPMCS\s3\Aws\Arn\S3\OutpostsAccessPointArn;
12 use Dudlewebs\WPMCS\s3\Aws\Arn\S3\MultiRegionAccessPointArn;
13 use Dudlewebs\WPMCS\s3\Aws\Arn\S3\OutpostsArnInterface;
14 use Dudlewebs\WPMCS\s3\Aws\CommandInterface;
15 use Dudlewebs\WPMCS\s3\Aws\Endpoint\PartitionEndpointProvider;
16 use Dudlewebs\WPMCS\s3\Aws\Exception\InvalidRegionException;
17 use Dudlewebs\WPMCS\s3\Aws\Exception\UnresolvedEndpointException;
18 use Dudlewebs\WPMCS\s3\Aws\S3\Exception\S3Exception;
19 use InvalidArgumentException;
20 use Dudlewebs\WPMCS\s3\Psr\Http\Message\RequestInterface;
21 /**
22 * Checks for access point ARN in members targeting BucketName, modifying
23 * endpoint as appropriate
24 *
25 * @internal
26 */
27 class BucketEndpointArnMiddleware
28 {
29 use EndpointRegionHelperTrait;
30 /** @var callable */
31 private $nextHandler;
32 /** @var array */
33 private $nonArnableCommands = ['CreateBucket'];
34 /** @var boolean */
35 private $isUseEndpointV2;
36 /**
37 * Create a middleware wrapper function.
38 *
39 * @param Service $service
40 * @param $region
41 * @param array $config
42 * @return callable
43 */
44 public static function wrap(Service $service, $region, array $config, $isUseEndpointV2)
45 {
46 return function (callable $handler) use($service, $region, $config, $isUseEndpointV2) {
47 return new self($handler, $service, $region, $config, $isUseEndpointV2);
48 };
49 }
50 public function __construct(callable $nextHandler, Service $service, $region, array $config = [], $isUseEndpointV2 = \false)
51 {
52 $this->partitionProvider = PartitionEndpointProvider::defaultProvider();
53 $this->region = $region;
54 $this->service = $service;
55 $this->config = $config;
56 $this->nextHandler = $nextHandler;
57 $this->isUseEndpointV2 = $isUseEndpointV2;
58 }
59 public function __invoke(CommandInterface $cmd, RequestInterface $req)
60 {
61 $nextHandler = $this->nextHandler;
62 $op = $this->service->getOperation($cmd->getName())->toArray();
63 if (!empty($op['input']['shape'])) {
64 $service = $this->service->toArray();
65 if (!empty($input = $service['shapes'][$op['input']['shape']])) {
66 foreach ($input['members'] as $key => $member) {
67 if ($member['shape'] === 'BucketName') {
68 $arnableKey = $key;
69 break;
70 }
71 }
72 if (!empty($arnableKey) && ArnParser::isArn($cmd[$arnableKey])) {
73 try {
74 // Throw for commands that do not support ARN inputs
75 if (\in_array($cmd->getName(), $this->nonArnableCommands)) {
76 throw new S3Exception('ARN values cannot be used in the bucket field for' . ' the ' . $cmd->getName() . ' operation.', $cmd);
77 }
78 if (!$this->isUseEndpointV2) {
79 $arn = ArnParser::parse($cmd[$arnableKey]);
80 $partition = $this->validateArn($arn);
81 $host = $this->generateAccessPointHost($arn, $req);
82 }
83 // Remove encoded bucket string from path
84 $path = $req->getUri()->getPath();
85 $encoded = \rawurlencode($cmd[$arnableKey]);
86 $len = \strlen($encoded) + 1;
87 if (\trim(\substr($path, 0, $len), '/') === "{$encoded}") {
88 $path = \substr($path, $len);
89 if (\substr($path, 0, 1) !== "/") {
90 $path = '/' . $path;
91 }
92 }
93 if (empty($path)) {
94 $path = '';
95 }
96 // Set modified request
97 if ($this->isUseEndpointV2) {
98 $req = $req->withUri($req->getUri()->withPath($path));
99 goto next;
100 }
101 $req = $req->withUri($req->getUri()->withPath($path)->withHost($host));
102 // Update signing region based on ARN data if configured to do so
103 if ($this->config['use_arn_region']->isUseArnRegion() && !$this->config['use_fips_endpoint']->isUseFipsEndpoint()) {
104 $region = $arn->getRegion();
105 } else {
106 $region = $this->region;
107 }
108 $endpointData = $partition(['region' => $region, 'service' => $arn->getService()]);
109 $cmd['@context']['signing_region'] = $endpointData['signingRegion'];
110 // Update signing service for Outposts and Lambda ARNs
111 if ($arn instanceof OutpostsArnInterface || $arn instanceof ObjectLambdaAccessPointArn) {
112 $cmd['@context']['signing_service'] = $arn->getService();
113 }
114 } catch (InvalidArnException $e) {
115 // Add context to ARN exception
116 throw new S3Exception('Bucket parameter parsed as ARN and failed with: ' . $e->getMessage(), $cmd, [], $e);
117 }
118 }
119 }
120 }
121 next:
122 return $nextHandler($cmd, $req);
123 }
124 private function generateAccessPointHost(BaseAccessPointArn $arn, RequestInterface $req)
125 {
126 if ($arn instanceof OutpostsAccessPointArn) {
127 $accesspointName = $arn->getAccesspointName();
128 } else {
129 $accesspointName = $arn->getResourceId();
130 }
131 if ($arn instanceof MultiRegionAccessPointArn) {
132 $partition = $this->partitionProvider->getPartitionByName($arn->getPartition(), 's3');
133 $dnsSuffix = $partition->getDnsSuffix();
134 return "{$accesspointName}.accesspoint.s3-global.{$dnsSuffix}";
135 }
136 $host = "{$accesspointName}-" . $arn->getAccountId();
137 $useFips = $this->config['use_fips_endpoint']->isUseFipsEndpoint();
138 $fipsString = $useFips ? "-fips" : "";
139 if ($arn instanceof OutpostsAccessPointArn) {
140 $host .= '.' . $arn->getOutpostId() . '.s3-outposts';
141 } else {
142 if ($arn instanceof ObjectLambdaAccessPointArn) {
143 if (!empty($this->config['endpoint'])) {
144 return $host . '.' . $this->config['endpoint'];
145 } else {
146 $host .= ".s3-object-lambda{$fipsString}";
147 }
148 } else {
149 $host .= ".s3-accesspoint{$fipsString}";
150 if (!empty($this->config['dual_stack'])) {
151 $host .= '.dualstack';
152 }
153 }
154 }
155 if (!empty($this->config['use_arn_region']->isUseArnRegion())) {
156 $region = $arn->getRegion();
157 } else {
158 $region = $this->region;
159 }
160 $region = \Dudlewebs\WPMCS\s3\Aws\strip_fips_pseudo_regions($region);
161 $host .= '.' . $region . '.' . $this->getPartitionSuffix($arn, $this->partitionProvider);
162 return $host;
163 }
164 /**
165 * Validates an ARN, returning a partition object corresponding to the ARN
166 * if successful
167 *
168 * @param $arn
169 * @return \Aws\Endpoint\Partition
170 */
171 private function validateArn($arn)
172 {
173 if ($arn instanceof AccessPointArnInterface) {
174 // Dualstack is not supported with Outposts access points
175 if ($arn instanceof OutpostsAccessPointArn && !empty($this->config['dual_stack'])) {
176 throw new UnresolvedEndpointException('Dualstack is currently not supported with S3 Outposts access' . ' points. Please disable dualstack or do not supply an' . ' access point ARN.');
177 }
178 if ($arn instanceof MultiRegionAccessPointArn) {
179 if (!empty($this->config['disable_multiregion_access_points'])) {
180 throw new UnresolvedEndpointException('Multi-Region Access Point ARNs are disabled, but one was provided. Please' . ' enable them or provide a different ARN.');
181 }
182 if (!empty($this->config['dual_stack'])) {
183 throw new UnresolvedEndpointException('Multi-Region Access Point ARNs do not currently support dual stack. Please' . ' disable dual stack or provide a different ARN.');
184 }
185 }
186 // Accelerate is not supported with access points
187 if (!empty($this->config['accelerate'])) {
188 throw new UnresolvedEndpointException('Accelerate is currently not supported with access points.' . ' Please disable accelerate or do not supply an access' . ' point ARN.');
189 }
190 // Path-style is not supported with access points
191 if (!empty($this->config['path_style'])) {
192 throw new UnresolvedEndpointException('Path-style addressing is currently not supported with' . ' access points. Please disable path-style or do not' . ' supply an access point ARN.');
193 }
194 // Custom endpoint is not supported with access points
195 if (!\is_null($this->config['endpoint']) && !$arn instanceof ObjectLambdaAccessPointArn) {
196 throw new UnresolvedEndpointException('A custom endpoint has been supplied along with an access' . ' point ARN, and these are not compatible with each other.' . ' Please only use one or the other.');
197 }
198 // Dualstack is not supported with object lambda access points
199 if ($arn instanceof ObjectLambdaAccessPointArn && !empty($this->config['dual_stack'])) {
200 throw new UnresolvedEndpointException('Dualstack is currently not supported with Object Lambda access' . ' points. Please disable dualstack or do not supply an' . ' access point ARN.');
201 }
202 // Global endpoints do not support cross-region requests
203 if ($this->isGlobal($this->region) && $this->config['use_arn_region']->isUseArnRegion() == \false && $arn->getRegion() != $this->region && !$arn instanceof MultiRegionAccessPointArn) {
204 throw new UnresolvedEndpointException('Global endpoints do not support cross region requests.' . ' Please enable use_arn_region or do not supply a global region' . ' with a different region in the ARN.');
205 }
206 // Get partitions for ARN and client region
207 $arnPart = $this->partitionProvider->getPartition($arn->getRegion(), 's3');
208 $clientPart = $this->partitionProvider->getPartition($this->region, 's3');
209 // If client partition not found, try removing pseudo-region qualifiers
210 if (!$clientPart->isRegionMatch($this->region, 's3')) {
211 $clientPart = $this->partitionProvider->getPartition(\Dudlewebs\WPMCS\s3\Aws\strip_fips_pseudo_regions($this->region), 's3');
212 }
213 if (!$arn instanceof MultiRegionAccessPointArn) {
214 // Verify that the partition matches for supplied partition and region
215 if ($arn->getPartition() !== $clientPart->getName()) {
216 throw new InvalidRegionException('The supplied ARN partition' . " does not match the client's partition.");
217 }
218 if ($clientPart->getName() !== $arnPart->getName()) {
219 throw new InvalidRegionException('The corresponding partition' . ' for the supplied ARN region does not match the' . " client's partition.");
220 }
221 // Ensure ARN region matches client region unless
222 // configured for using ARN region over client region
223 $this->validateMatchingRegion($arn);
224 // Ensure it is not resolved to fips pseudo-region for S3 Outposts
225 $this->validateFipsConfigurations($arn);
226 }
227 return $arnPart;
228 }
229 throw new InvalidArnException('Provided ARN was not a valid S3 access' . ' point ARN or S3 Outposts access point ARN.');
230 }
231 /**
232 * Checks if a region is global
233 *
234 * @param $region
235 * @return bool
236 */
237 private function isGlobal($region)
238 {
239 return $region == 's3-external-1' || $region == 'aws-global';
240 }
241 }
242